Uma alternativa moderna para importScripts().
Contexto
Os módulos ES são os favoritos dos desenvolvedores há algum tempo. Além de vários outros benefícios, eles oferecem a promessa de um formato de módulo universal em que o código compartilhado pode ser lançado uma vez e executado em navegadores e em ambientes de execução alternativos, como Node.js. Embora todos os navegadores modernos ofereçam algum suporte ao módulo ES, nem todos oferecem suporte em todos os lugares em que o código pode ser executado. Especificamente, o suporte para importar módulos ES dentro de um service worker do navegador está começando a ficar mais disponível.
Este artigo detalha o estado atual do suporte ao módulo ES em service workers em navegadores comuns, além de algumas armadilhas a serem evitadas e práticas recomendadas para enviar 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 um código de configuração compartilhado com outros ambientes de execução com suporte a módulos ES.
Tentar compartilhar o código dessa maneira antes dos módulos ES envolvia o uso de formatos de módulo "universais" mais antigos, como o UMD, que incluía boilerplate desnecessário, e escrever código que fazia mudanças em variáveis expostas globalmente.
Os scripts importados por módulos ES podem acionar o fluxo de
atualização
do service worker se o conteúdo deles mudar, correspondendo ao
comportamento
do
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
dinamicamente,
usando o método import()
. No momento, apenas a sintaxe
estática é aceita em um service worker.
Essa limitação é análoga a uma
restrição semelhante
colocada no uso de importScripts()
. As chamadas dinâmicas para importScripts()
não
funcionam dentro de um service worker, e todas as chamadas importScripts()
, que são
inerentemente síncronas, precisam ser concluídas antes que o service worker conclua a
fase install
. Essa restrição garante que o navegador saiba e possa
armazenar em cache implicitamente todo o código JavaScript necessário para a implementação
de um worker de serviço durante a instalação.
Eventualmente, essa restrição poderá ser suspensa, e as importações de módulo ES dinâmico poderão ser permitidas. Por enquanto, use apenas a sintaxe estática dentro de um service worker.
E os outros workers?
O suporte a
módulos ES em workers "dedicados" (aqueles
criados com new Worker('...', {type: 'module'})
) é mais difundido e
tem suporte no Chrome e no Edge desde a
versão 80, bem como nas
versões recentes do Safari.
As importações estáticas e dinâmicas de módulos ES são compatíveis com workers dedicados.
O Chrome e o Edge têm suporte a módulos ES em workers compartilhados desde a versão 83, mas nenhum outro navegador oferece suporte no momento.
Não há suporte para a importação de mapas
Os mapas de importação permitem que ambientes de execução reescrevam especificadores de módulo para, por exemplo, adicionar o URL de uma CDN preferencial em que os módulos do ES podem ser carregados.
Embora o Chrome e o Edge versão 89 e mais recentes ofereçam suporte a mapas de importação, eles não podem ser usados com workers de serviço.
Suporte ao navegador
Os módulos ES em service workers são compatíveis com o Chrome e o Edge a partir da versão 91.
O Safari adicionou suporte à versão 122 de pré-lançamento da tecnologia, e os desenvolvedores devem esperar que essa funcionalidade seja lançada na versão estável do Safari no futuro.
Exemplo de código
Este é um exemplo básico de uso de um módulo ES compartilhado no contexto window
de um app da Web, além de registrar um worker de serviço 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 oferecessem suporte a módulos ES em service workers, mas, no momento em que este artigo foi escrito, não era o caso.
Para acomodar navegadores que não têm suporte integrado, é possível executar o
script do worker de serviço usando um
bundler compatível com o módulo ES para criar um
worker de serviço que inclua todo o código do módulo inline e funcione em
navegadores mais antigos. Como alternativa, se os módulos que você está tentando importar já estiverem
disponíveis em pacotes nos formatos
IIFE ou
UMD, será possível importá-los usando
importScripts()
.
Depois que você tiver duas versões do service worker disponíveis, uma que usa módulos ES e a outra que não usa, será necessário detectar o que o navegador atual oferece suporte e registrar o script do service worker correspondente. As práticas recomendadas para detectar suporte estão em fluxo, mas você pode seguir a discussão neste problema do GitHub para ver recomendações.
_Foto de Vlado Paunovic no Unsplash_