Service worker più aggiornati per impostazione predefinita

Jacopo Posnick
Jacopo Posnick

tl;dr

A partire da Chrome 68, per impostazione predefinita le richieste HTTP che verificano la presenza di aggiornamenti dello script del service worker non verranno più evase dalla cache HTTP. Questo approccio risolve un problema comune degli sviluppatori, in base al quale l'impostazione di un'intestazione Cache-Control involontaria nello script del service worker potrebbe causare aggiornamenti in ritardo.

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 attiva il flusso di aggiornamento dei service worker, proprio come farebbe una modifica al service worker di primo livello.

Contesto

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

Ai fini di questo articolo, supponiamo che l'URL sia /service-worker.js e che contenga una singola chiamata a importScripts(), che carica il 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 (come la maggior parte dei recuperi). Ciò significava che se lo script era stato originariamente inviato con Cache-Control: max-age=600, gli aggiornamenti entro i 600 secondi successivi (10 minuti) non verrebbero inviati alla rete, quindi l'utente potrebbe non ricevere la versione più aggiornata del service worker. Tuttavia, se max-age era maggiore di 86400 (24 ore), verrebbe considerato come se fosse 86400, per evitare che gli utenti siano bloccati per sempre con una determinata versione.

A partire dalla versione 68, la cache HTTP verrà ignorata quando vengono richiesti aggiornamenti allo script del service worker, perciò le applicazioni web esistenti potrebbero registrare un aumento della frequenza delle richieste per lo script del service worker. Le richieste per importScripts continueranno a essere inviate tramite la cache HTTP. Tuttavia, si tratta dell'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 durante la chiamata a navigator.serviceWorker.register(): il parametro updateViaCache. Può utilizzare uno dei tre valori seguenti: 'imports', 'all' o 'none'.

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

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

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

  • Se il criterio viene impostato su 'none', la cache HTTP non viene consultata quando vengono richieste 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 garantirà che la cache HTTP non venga mai consultata durante la verifica degli aggiornamenti dello script /service-worker.js o di 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 per gli script importati

Prima di Chrome 78, qualsiasi script del service worker caricato tramite importScripts() veniva recuperato una sola volta (verificando prima la cache HTTP o tramite la rete, a seconda della configurazione di updateViaCache). Dopo il recupero iniziale, viene archiviato internamente dal browser e non viene mai recuperato.

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

A partire da Chrome 78, ogni volta che viene eseguito un controllo degli aggiornamenti per un file del service worker di primo livello, vengono eseguiti contemporaneamente anche 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 completati dalla cache HTTP se updateViaCache è impostato su 'all' o 'imports' (che è il valore predefinito) oppure direttamente sulla rete se updateViaCache è impostato su 'none'.

Se il controllo degli aggiornamenti per uno script importato genera una differenza byte per byte rispetto a quanto precedentemente archiviato dal service worker, questo attiva 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 che Firefox ha implementato diversi anni fa, in Firefox 56. Anche Safari implementa già questo comportamento.

Cosa devono fare gli sviluppatori?

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

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

Dato che nelle versioni precedenti del browser potrebbe esserci una long-tail di utenti, è comunque una buona idea continuare a impostare l'intestazione HTTP Cache-Control: max-age=0 sugli script dei service worker, anche se i browser più recenti potrebbero ignorarli.

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

Pubblicazione di script importati

A partire da Chrome 78, gli sviluppatori potrebbero notare un numero maggiore di richieste HTTP in entrata per le risorse caricate tramite importScripts(), poiché ora verrà controllata la disponibilità 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 nei relativi URL e utilizza il comportamento updateViaCache predefinito di 'imports'.

In alternativa, se vuoi che gli script importati siano controllati 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 e best practice per la massima età", di Jake Archibald, sono consigliati a tutti gli sviluppatori che eseguono il deployment di qualsiasi cosa sul web.