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 a módulos ES, eles não oferecem suporte em todos os lugares em que o código pode ser executado. Especificamente, o suporte para importar módulos ES dentro do service worker de um navegador está começando a se tornar mais amplamente disponível.
Neste artigo, detalhamos o estado atual do suporte a módulos 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 que oferecem suporte a módulos ES.
Antes dos módulos ES, para compartilhar código dessa forma, era preciso usar formatos de módulo "universais" mais antigos, como o UMD, que incluem código boilerplate desnecessário, e escrever código que fizesse 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
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
dinamicamente,
usando o método import(). Em um service worker, apenas a sintaxe estática
é aceita no momento.
Essa limitação é semelhante a uma
restrição parecida
aplicada ao 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 conheça e possa armazenar em cache implicitamente todo o código JavaScript necessário para a implementação de um service worker durante a instalação.
No futuro, essa restrição poderá ser suspensa, e as importações de módulos ES dinâmicos poderão ser permitidas. Por enquanto, use apenas a sintaxe estática dentro de um service worker.
E os outros trabalhadores?
O suporte para
módulos ES em workers "dedicados",
criados com new Worker('...', {type: 'module'}), é mais amplo e
está disponível no Chrome e no Edge desde
a versão 80, além de
versões recentes do Safari.
As importações de módulos ES estáticas e dinâmicas são compatíveis com workers dedicados.
O Chrome e o Edge oferecem 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 mapas de importação
Os mapas de importação permitem que os ambientes de execução reescrevam especificadores de módulo para, por exemplo, adicionar o URL de uma CDN preferencial de onde os módulos ES podem ser carregados.
Embora o Chrome e o Edge na versão 89 e mais recentes ofereçam suporte a mapas de importação, no momento eles não podem ser usados com service workers.
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 na versão Technology Preview 122, e os desenvolvedores podem 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 como usar um módulo ES compartilhado no contexto window
de um app da Web e, ao mesmo tempo, 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, execute o
script do service worker usando um
empacotador compatível com o módulo ES para criar um
service worker 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 de disponibilizar duas versões do service worker, uma que usa módulos ES e outra que não, você precisará detectar o que o navegador atual aceita e registrar o script do service worker correspondente. As práticas recomendadas para detectar suporte estão em constante mudança, mas você pode acompanhar a discussão neste problema do GitHub para recomendações.