Moduli ES nei service worker

Un'alternativa moderna a importScripts().

Contesto

I moduli ES sono tra i preferiti degli sviluppatori da un po' di tempo. Oltre a una serie di altri vantaggi, offrono la promessa di un formato di modulo universale in cui il codice condiviso può essere rilasciato una volta ed eseguito nei browser e in runtime alternativi come Node.js. Sebbene tutti i browser moderni offrano il supporto di alcuni moduli ES, non tutti offrono supporto ovunque in cui è possibile eseguire il codice. In particolare, il supporto per l'importazione dei moduli ES all'interno di un service worker di un browser sta iniziando a diventare disponibile su larga scala.

Questo articolo descrive lo stato attuale del supporto dei moduli ES nei service worker nei browser più comuni, insieme ad alcuni suggerimenti da evitare e alle best practice per la spedizione di codice dei service worker compatibile con le versioni precedenti.

Casi d'uso

Il caso d'uso ideale per i moduli ES all'interno dei service worker è il caricamento di una libreria moderna o di un codice di configurazione condiviso con altri runtime che supportano i moduli ES.

Il tentativo di condividere il codice in questo modo prima dei moduli ES comportava l'utilizzo di formati di moduli "universali" meno recenti, come UMD, che includono boilerplate non necessari, e la scrittura di codice che apporta modifiche alle variabili esposte a livello globale.

Gli script importati tramite moduli ES possono attivare il flusso di aggiornamento del service worker se i relativi contenuti cambiano, in base al comportamento di importScripts().

Limitazioni attuali

Solo importazioni statiche

I moduli ES possono essere importati in due modi: staticamente, utilizzando la sintassi import ... from '...' o dinamica, utilizzando il metodo import(). Al momento all'interno di un service worker è supportata solo la sintassi statica.

Questa limitazione è analoga a una restrizione simile applicata all'utilizzo di importScripts(). Le chiamate dinamiche a importScripts() non funzionano all'interno di un service worker e tutte le chiamate importScripts(), che sono intrinsecamente sincrone, devono essere completate prima che il service worker completi la sua fase install. Questa limitazione garantisce che il browser sia a conoscenza di tutto il codice JavaScript necessario per l'implementazione di un service worker durante l'installazione e sia in grado di memorizzarlo nella cache implicitamente.

Questa limitazione potrebbe essere rimossa e le importazioni dinamiche di moduli ES potrebbero essere consentite. Per ora, assicurati di utilizzare solo la sintassi statica all'interno di un service worker.

E gli altri lavoratori?

Il supporto dei moduli ES nei worker "dedicati", ovvero costruiti con new Worker('...', {type: 'module'}), è più diffuso ed è supportato in Chrome ed Edge sin dalla versione 80, nonché nelle versioni recenti di Safari. Le importazioni di moduli ES statiche e dinamiche sono supportate nei worker dedicati.

Chrome ed Edge supportano i moduli ES nei lavoratori condivisi dalla versione 83, ma al momento non sono disponibili altri browser.

Nessun supporto per l'importazione delle mappe

Le mappe di importazione consentono agli ambienti di runtime di riscrivere gli indicatori dei moduli, ad esempio anteporre l'URL di una CDN preferita da cui possono essere caricati i moduli ES.

Sebbene Chrome ed Edge e la versione 89 e successive supportino le mappe di importazione, al momento non possono essere utilizzate con i service worker.

Supporto del browser

I moduli ES nei service worker sono supportati in Chrome ed Edge a partire dalla versione 91.

Safari ha aggiunto il supporto nella release 122 dell'anteprima tecnologica e gli sviluppatori dovrebbero aspettarsi che in futuro questa funzionalità verrà rilasciata nella versione stabile di Safari.

Codice di esempio

Questo è un esempio base di utilizzo di un modulo ES condiviso nel contesto window di un'app web, registrazione di un service worker che utilizza lo stesso modulo 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);
    // ...
  })());
});

Compatibilità con le versioni precedenti

L'esempio precedente andrebbe bene se tutti i browser supportavano i moduli ES nei service worker, ma al momento della stesura del presente documento, non è così.

Per supportare i browser che non hanno il supporto integrato, puoi eseguire lo script del service worker tramite un bundler compatibile con il modulo ES per creare un service worker che includa tutto il codice del modulo in linea e funzionerà nei browser meno recenti. In alternativa, se i moduli che stai tentando di importare sono già disponibili in bundle nei formati IIFE o UMD, puoi importarli utilizzando importScripts().

Quando sono disponibili due versioni del service worker, una che utilizza moduli ES e l'altra che no, dovrai rilevare ciò che supporta il browser corrente e registrare lo script del service worker corrispondente. Al momento, le best practice per rilevare l'assistenza sono in continua evoluzione, ma puoi seguire la discussione in questo problema GitHub per suggerimenti.

_Foto di Vlado Paunovic su Unsplash_