Service worker più aggiornati per impostazione predefinita

tl;dr

A partire da Chrome 68, le richieste HTTP che verificano la presenza di aggiornamenti dello script del service worker non verranno più soddisfatte dalla cache HTTP per impostazione predefinita. Questa operazione risolve un problema comune degli sviluppatori, per cui l'impostazione di un'intestazione Cache-Control involontaria nello script del service worker potrebbe causare aggiornamenti ritardati.

Se hai già disattivato la memorizzazione nella cache HTTP per lo script /service-worker.js pubblicandolo con Cache-Control: max-age=0, non dovresti notare alcuna modifica a causa del nuovo comportamento predefinito.

Inoltre, a partire da Chrome 78, il confronto byte per byte verrà applicato agli script caricati in un service worker tramite importScripts(). Qualsiasi modifica apportata a uno script importato attiverà il flusso di aggiornamento del service worker, proprio come farebbe una modifica al service worker di primo livello.

Contesto

Ogni volta che accedi a una nuova pagina nell'ambito di un service worker, chiami esplicitamente registration.update() da JavaScript o quando un service worker viene "attivato" tramite un evento push o sync, il browser, in parallelo, richiederà la risorsa JavaScript passata in origine alla chiamata navigator.serviceWorker.register() per cercare aggiornamenti dello script del service worker.

Ai fini di questo articolo, supponiamo che il suo URL sia /service-worker.js e che contenga una singola chiamata a importScripts(), che carica un codice aggiuntivo eseguito all'interno del service worker:

// Inside our /service-worker.js file:
importScripts('path/to/import.js');

// Other top-level code goes here.

Cosa cambierà?

Prima di Chrome 68, la richiesta di aggiornamento di /service-worker.js veniva effettuata tramite la cache HTTP (così come per la maggior parte dei recuperi). Questo significava che se lo script è stato originariamente inviato con Cache-Control: max-age=600, gli aggiornamenti entro i successivi 600 secondi (10 minuti) non verrebbero inviati alla rete, quindi l'utente potrebbe non ricevere la versione più aggiornata del service worker. Tuttavia, se il valore di max-age fosse superiore a 86.400 (24 ore), viene trattato come se fosse 86.400, per evitare che gli utenti rimangano bloccati per sempre su una determinata versione.

A partire dalla versione 68, la cache HTTP verrà ignorata quando verranno richiesti aggiornamenti allo script del service worker, quindi le applicazioni web esistenti potrebbero notare un aumento della frequenza delle richieste per lo script del service worker. Le richieste per importScripts verranno comunque inviate tramite la cache HTTP. Tuttavia, questa è solo l'impostazione predefinita: è disponibile una nuova opzione di registrazione, updateViaCache, che offre il controllo su questo comportamento.

updateViaCache

Ora gli sviluppatori possono passare una nuova opzione quando chiamano navigator.serviceWorker.register(): il parametro updateViaCache. Può assumere uno dei tre valori seguenti: 'imports', 'all' o 'none'.

I valori determinano se e come entra in gioco la cache HTTP standard del browser quando si effettua la richiesta HTTP per verificare la presenza di risorse dei service worker aggiornate.

  • Se il criterio viene impostato su 'imports', la cache HTTP non verrà mai consultata durante il controllo della disponibilità di aggiornamenti dello script /service-worker.js, ma lo sarà durante il recupero di eventuali script importati (nel nostro esempio path/to/import.js). Questo è il comportamento predefinito e corrisponde a quello a partire da Chrome 68.

  • Se il criterio viene impostato su 'all', la cache HTTP verrà consultata quando si effettuano richieste sia per lo script /service-worker.js di primo livello, sia per gli eventuali script importati all'interno del worker di servizio, ad esempio path/to/import.js. Questa opzione corrisponde al comportamento precedente in Chrome prima della 68.

  • Se il criterio viene impostato su 'none', la cache HTTP non verrà consultata quando si effettuano richieste per il /service-worker.js di primo livello o per eventuali script importati, come l'ipotetico path/to/import.js.

Ad esempio, il seguente codice registrerà un service worker e assicura che la cache HTTP non venga mai consultata durante il controllo della disponibilità di aggiornamenti per lo script /service-worker.js o per eventuali script a cui viene fatto riferimento tramite importScripts() all'interno di /service-worker.js:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js', {
    updateViaCache: 'none',
    // Optionally, set 'scope' here, if needed.
  });
}

Verifica la presenza di aggiornamenti degli script importati

Prima di Chrome 78, qualsiasi script del service worker caricato tramite importScripts() veniva recuperato una sola volta (prima verificandolo con la cache HTTP o tramite la rete, a seconda della configurazione di updateViaCache). Dopo questo recupero iniziale, i dati vengono archiviati internamente dal browser e non vengono mai recuperati di nuovo.

L'unico modo per forzare un service worker già installato a rilevare le modifiche in uno script importato era modificare l'URL dello script, in genere aggiungendo un valore di servizio (ad es. importScripts('https://example.com/v1.1.0/index.js')) o includendo un hash dei contenuti (ad es. importScripts('https://example.com/index.abcd1234.js')). Un effetto collaterale della modifica dell'URL importato è che la modifica dei contenuti dello script del service worker di primo livello attiva la modifica del flusso dei contenuti del worker di servizio di livello superiore, che a sua volta attiva il flusso dei contenuti del worker di servizio di livello superiore.

A partire da Chrome 78, ogni volta che viene eseguito un controllo degli aggiornamenti per un file del Service worker di primo livello, vengono effettuati contemporaneamente dei controlli per determinare se i contenuti degli script importati sono stati modificati. A seconda delle intestazioni Cache-Control utilizzate, questi controlli degli script importati potrebbero essere eseguiti dalla cache HTTP se updateViaCache è impostato su 'all' o 'imports' (il valore predefinito) oppure i controlli potrebbero essere eseguiti direttamente sulla rete se updateViaCache è impostato su 'none'.

Se un controllo degli aggiornamenti per uno script importato comporta una differenza byte per byte rispetto a quanto precedentemente archiviato dal service worker, questo attiverà a sua volta il flusso di aggiornamento completo del service worker, anche se il file del service worker di primo livello rimane lo stesso.

Il comportamento di Chrome 78 corrisponde a quello implementato da Firefox diversi anni fa, con Firefox 56. Questo comportamento viene già implementato in Safari.

Cosa devono fare gli sviluppatori?

Se hai disattivato in modo efficace la memorizzazione nella cache HTTP per lo script /service-worker.js pubblicandolo con Cache-Control: max-age=0 (o un valore simile), non dovresti notare alcuna modifica a causa del nuovo comportamento predefinito.

Se pubblichi il tuo script /service-worker.js con la memorizzazione nella cache HTTP abilitata, intenzionalmente o perché è solo l'impostazione predefinita per l'ambiente di hosting, potresti iniziare a vedere un aumento di richieste HTTP aggiuntive per /service-worker.js effettuate contro il tuo server: si tratta di richieste che venivano soddisfatte dalla cache HTTP. Se vuoi continuare a consentire al valore dell'intestazione Cache-Control di influire sull'aggiornamento di /service-worker.js, devi iniziare a impostare esplicitamente updateViaCache: 'all' quando registri il tuo service worker.

Dato che le versioni precedenti del browser potrebbero avere più utenti in coda, è comunque una buona idea continuare a impostare l'intestazione HTTP Cache-Control: max-age=0 negli script dei service worker, anche se i browser più recenti potrebbero ignorarli.

Gli sviluppatori possono sfruttare questa opportunità per decidere se disattivare esplicitamente la memorizzazione nella cache HTTP per gli script importati e aggiungere updateViaCache: 'none' alla registrazione dei service worker, se opportuno.

Pubblicazione di script importati

A partire da Chrome 78, gli sviluppatori potrebbero vedere un numero maggiore di richieste HTTP in entrata per le risorse caricate tramite importScripts(), poiché ora verranno controllate la presenza di aggiornamenti.

Se vuoi evitare questo traffico HTTP aggiuntivo, imposta intestazioni Cache-Control di lunga durata durante la pubblicazione di script che includono semver o hash negli URL e affidati al comportamento updateViaCache predefinito di 'imports'.

In alternativa, se vuoi che gli script importati vengano controllati frequenti per verificare la presenza di aggiornamenti frequenti, assicurati di pubblicarli con Cache-Control: max-age=0 o di utilizzare updateViaCache: 'none'.

Per approfondire

"The Service Worker Lifecycle" e "Memorizzazione nella cache best practice & max-age gochas", entrambi di Jake Archibald, sono consigliati a tutti gli sviluppatori che eseguono il deployment di qualsiasi cosa sul web.