Módulos ES em service workers

Uma alternativa moderna para importScripts().

Contexto

Módulos ES já são um dos favoritos dos desenvolvedores há algum tempo. Além de um vários outros benefícios, oferecem a promessa de um formato de módulo universal em que o código compartilhado pode ser lançados uma vez e executados em navegadores e em ambientes de execução alternativos, como Node.js: todos os navegadores modernos oferecem suporte a módulos ES, nem todos oferecem suporte em todos os lugares código pode ser executado. Especificamente, o suporte à importação de módulos ES dentro de uma do navegador service worker está começando a se tornar mais amplamente disponível.

Este artigo detalha o estado atual do suporte ao módulo ES em service workers em navegadores comuns, junto com algumas pegadinhas que devem ser evitadas e as práticas recomendadas para envio de código de service worker compatível com versões anteriores.

Casos de uso

O caso de uso ideal para módulos ES dentro de service workers é carregar uma biblioteca moderna ou código de configuração compartilhado com outros ambientes de execução que oferecem suporte a módulos ES.

A tentativa de compartilhar código dessa forma antes dos módulos ES implicava o uso de versões "universal" formatos de módulo diferentes, como UMD, que incluem código boilerplate desnecessário e código que fez alterações variáveis.

Os scripts importados por módulos ES podem acionar o service worker atualizar se o conteúdo mudar, correspondendo comportamento de importScripts().

Limitações atuais

Somente importações estáticas

Os módulos ES podem ser importados de duas maneiras: estaticamente, usando a sintaxe import ... from '...' ou de forma dinâmica, usando o método import(). Dentro de um service worker, somente o tem suporte no momento.

Essa limitação é análoga a uma restrição semelhante colocado no uso de importScripts(). As chamadas dinâmicas para importScripts() não trabalham dentro de um service worker e todas as chamadas importScripts(), que são que é inerentemente síncrona, deve ser concluída antes que o service worker install. Essa restrição garante que o navegador saiba e saiba armazenar em cache implicitamente, todo o código JavaScript necessário para a operação durante a instalação.

Essa restrição pode ser removida, e o ES dinâmico importações de módulo podem ser permitidos. Por enquanto, use apenas a sintaxe estática dentro do um service worker.

E os outros workers?

Suporte para Módulos ES em "dedicado" workers: aqueles que construída com new Worker('...', {type: 'module'}), é mais generalizada e tem suporte no Chrome e no Edge desde versão 80, bem como versões recentes do Safari. As importações de módulo ES estático e dinâmico são compatíveis com workers dedicados.

O Chrome e o Edge têm módulos ES compatíveis com o workers compartilhados desde a versão 83, mas ainda outros navegadores já oferecem suporte.

Não há suporte para a importação de mapas

Importar mapas permitem ambientes de execução para reescrever os especificadores de módulo, por exemplo, adicionando o URL de uma CDN preferencial de onde os módulos ES podem ser carregados.

Enquanto o Chrome e o Edge versão 89 e mais recente oferecem suporte à importação de mapas, eles atualmente não pode ser usado com o serviço trabalhadores

Suporte ao navegador

Os módulos ES em service workers são compatíveis com o Chrome e o Edge, começando com versão 91.

O Safari adicionou suporte ao Versão 122 da Prévia da tecnologia, Essa funcionalidade será lançada na versão estável do Safari no futuro.

Exemplo de código

Este é um exemplo básico de como usar um módulo ES compartilhado no window de um app da Web. contexto, além de registrar um service worker que usa o mesmo módulo ES:

// Inside config.js:
export const cacheName = 'my-cache';
// Inside your web app:
<script type="module">
  import {cacheName} from './config.js';
  // Do something with cacheName.

  await navigator.serviceWorker.register('es-module-sw.js', {
    type: 'module',
  });
</script>
// Inside es-module-sw.js:
import {cacheName} from './config.js';

self.addEventListener('install', (event) => {
  event.waitUntil((async () => {
    const cache = await caches.open(cacheName);
    // ...
  })());
});

Compatibilidade com versões anteriores

O exemplo acima funcionaria bem se todos os navegadores fossem compatíveis com módulos ES em service workers, mas, no momento, esse não é o caso.

Para acomodar navegadores que não têm suporte integrado, você pode executar seu script do service worker por meio de uma bundler compatível com módulo ES para criar um service worker que inclui todo o código do módulo inline e funcionará navegadores mais antigos. Como alternativa, se os módulos que você estiver tentando importar forem já está disponível no pacote IIFE ou UMD, podem ser importados usando importScripts().

Assim que você tiver duas versões do service worker disponíveis, uma que use ES módulos e outro que não tem, será preciso detectar o que com suporte do navegador e registre o script do service worker correspondente. Os melhores as práticas recomendadas para detectar suporte estão em constante fluxo, mas é possível acompanhar discussão neste Problema no GitHub para recomendações.

_Foto por Vlado Paunovic em Unsplash_