Moduli ES nei service worker

Un'alternativa moderna a importScripts().

Contesto

I moduli ES sono tra i preferiti dagli sviluppatori da un po' di tempo. Oltre a una serie di altri vantaggi, offrono la promessa di un formato di moduli universale in cui il codice condiviso può essere rilasciato una sola volta ed eseguito nei browser e in runtime alternativi come Node.js. Sebbene tutti i browser moderni offrano un certo supporto per moduli ES, non tutti supportano ovunque sia possibile eseguire il codice. Nello specifico, il supporto per l'importazione di moduli ES all'interno di un service worker di un browser sta iniziando a essere 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 del 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 prevedeva l'uso di formati di moduli "universali" meno recenti come UMD che includono boilerplate non necessari e la scrittura di codice per apportare 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 dinamicamente, con il metodo import(). All'interno di un service worker, al momento è 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 conosca e possa memorizzare nella cache implicitamente tutto il codice JavaScript necessario per l'implementazione di un service worker durante l'installazione.

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

E gli altri lavoratori?

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

Chrome ed Edge supportano moduli ES nei lavoratori condivisi dalla versione 83, ma al momento nessun altro browser offre questo supporto.

Nessun supporto per l'importazione delle mappe

L'importazione delle mappe consente agli ambienti di runtime di riscrivere gli identificatori dei moduli, ad esempio per anteporre l'URL di una rete CDN preferita da cui possono essere caricati i moduli ES.

Sebbene la versione 89 e successive di Chrome ed Edge supportino l'importazione delle mappe, 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 di Technology Preview e gli sviluppatori dovrebbero aspettarsi che questa funzionalità venga rilasciata nella versione stabile di Safari in futuro.

Codice di esempio

Questo è un esempio di base dell'utilizzo di un modulo ES condiviso nel contesto window di un'app web, nonché della 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 riportato sopra funzionerebbe bene se tutti i browser supportino i moduli ES nei service worker, ma, al momento della stesura di questo articolo, non è così.

Per supportare i browser che non dispongono del 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 funzioni nei browser precedenti. 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 no), devi rilevare ciò che è supportato dal browser corrente e registrare lo script del service worker corrispondente. Le best practice per il rilevamento del supporto sono attualmente variabili, ma puoi seguire la discussione in questo problema di GitHub per ricevere consigli.

_Foto di Vlado Paunovic su Unsplash_