Un'alternativa moderna a importScripts().
Sfondo
I moduli ES sono tra i preferiti degli sviluppatori da un po' di tempo. Oltre a un numero di altri vantaggi, offrono la promessa di un formato di modulo universale in cui il codice condiviso può essere rilasciato una volta ed eseguito in browser e in ambienti di runtime alternativi come Node.js. Anche se tutti i browser moderni offrono un certo supporto per i moduli ES, non tutti offrono supporto ovunque puoi eseguire il codice. Nello specifico, il supporto per l'importazione dei moduli ES all'interno del service worker di un browser sta appena iniziando a essere disponibile su larga scala.
Questo articolo descrive lo stato attuale del supporto dei moduli ES nei worker di servizio su browser comuni, oltre ad alcuni problemi da evitare e le best practice per la pubblicazione di codice di worker di servizio compatibile con le versioni precedenti.
Casi d'uso
Il caso d'uso ideale per i moduli ES all'interno dei worker di servizio è il caricamento di una libreria o di un codice di configurazione moderno condiviso con altri ambienti di 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 codice boilerplate non necessario e la scrittura di codice che apportava modifiche alle variabili esposte a livello globale.
Gli script importati tramite i moduli ES possono attivare il flusso di
aggiornamento
del worker di servizio se i relativi contenuti cambiano, corrispondendo 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,
utilizzando il metodo import()
. All'interno di un worker di servizio, al momento è supportata solo la sintassi statica.
Questa limitazione è analoga a una
limitazione simile
apposta all'utilizzo di importScripts()
. Le chiamate dinamiche a importScripts()
non funzionano all'interno di un servizio worker e tutte le chiamate importScripts()
, che sono intrinsecamente sincrone, devono essere completate prima che il servizio worker completi la fase install
. Questa limitazione garantisce che il browser conosca e sia in grado di memorizzare nella cache in modo implicito tutto il codice JavaScript necessario per l'implementazione di un worker di servizio durante l'installazione.
In futuro, questa limitazione potrebbe essere rimossa e le importazioni di moduli ES dinamici potrebbero essere consentite. Per il momento, assicurati di utilizzare la sintassi statica solo all'interno di un worker di servizio.
E gli altri lavoratori?
Il supporto per i
moduli ES nei worker "dedicate", ovvero quelli
costruiti con new Worker('...', {type: 'module'})
, è più diffuso ed è supportato in Chrome e Edge dalla
versione 80, nonché nelle
versioni recenti di Safari.
Le importazioni di moduli ES statici e dinamici sono supportate nei worker dedicati.
Chrome ed Edge supportano i moduli ES nei worker condivisi da versione 83, ma nessun altro browser offre il supporto al momento.
Nessun supporto per l'importazione delle mappe
Le mappe di importazione consentono agli ambienti di runtime di riscrivere gli specificatori dei moduli, ad esempio anteponendo l'URL di una CDN preferita da cui è possibile caricare i moduli ES.
Sebbene Chrome e Edge versione 89 e successive supportino le mappe di importazione, al momento non possono essere utilizzati con i worker di servizio.
Supporto browser
I moduli ES nei worker di servizio sono supportati in Chrome ed Edge a partire dalla versione 91.
Safari ha aggiunto il supporto nella release Technology Preview 122, e gli sviluppatori dovrebbero aspettarsi di vedere questa funzionalità rilasciata in futuro nella versione stabile di Safari.
Esempio di codice
Questo è un esempio di base dell'utilizzo di un modulo ES condiviso nel window
contesto di un'app web, registrando al contempo 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 perfettamente se tutti i browser supportassero i moduli ES nei worker di servizio, ma al momento non è così.
Per supportare i browser che non dispongono del supporto integrato, puoi eseguire lo script del tuo worker di servizio tramite un bundler compatibile con i moduli ES per creare un worker di servizio che includa tutto il codice del modulo in linea e funzioni 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()
.
Una volta che hai a disposizione due versioni del tuo servizio worker, una che utilizza i moduli ES e l'altra no, devi rilevare cosa supporta il browser corrente e registrare lo script del servizio worker corrispondente. Le migliori pratiche per il rilevamento dell'assistenza sono attualmente in evoluzione, ma puoi seguire la discussione in questo problema GitHub per ricevere consigli.
_Foto di Vlado Paunovic su Unsplash_