Service worker più aggiornati per impostazione predefinita

tl;dr

A partire da Chrome 68, le richieste HTTP che verificano la presenza di aggiornamenti allo script del worker del servizio non verranno più soddisfatte dalla cache HTTP per impostazione predefinita. In questo modo, puoi aggirare un punto critico comune per gli sviluppatori, in cui l'impostazione di un'intestazione Cache-Control involontaria nello script del tuo worker di servizio potrebbe portare a 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 worker di servizio tramite importScripts(). Qualsiasi modifica apportata a uno script importato attiverà il flusso di aggiornamento del servizio worker, come farebbe una modifica al servizio worker di primo livello.

Sfondo

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

Ai fini di questo articolo, supponiamo che l'URL sia /service-worker.js e che contenga una singola chiamata a importScripts(), che carica codice aggiuntivo eseguito all'interno del servizio 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 per /service-worker.js veniva effettuata tramite la cache HTTP, come la maggior parte dei recuperi. Ciò significa che se lo script è stato inviato originariamente con Cache-Control: max-age=600, gli aggiornamenti nei 600 secondi successivi (10 minuti) non verranno inviati alla rete, pertanto l'utente potrebbe non ricevere la versione più aggiornata del servizio worker. Tuttavia, se max-age fosse superiore a 86400 (24 ore), verrebbe considerato come 86400 per evitare che gli utenti rimangano bloccati con una determinata versione per sempre.

A partire dalla versione 68, la cache HTTP verrà ignorata quando si richiedono aggiornamenti allo script del servizio worker, pertanto le applicazioni web esistenti potrebbero registrare un aumento della frequenza delle richieste per lo script del servizio worker. Le richieste di importScripts continueranno a passare attraverso la cache HTTP. Tuttavia, si tratta solo 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 quando chiamano navigator.serviceWorker.register(): il parametro updateViaCache. Deve essere uno dei tre valori: 'imports', 'all' o 'none'.

I valori determinano se e come la cache HTTP standard del browser viene attivata quando viene effettuata la richiesta HTTP per verificare la presenza di risorse di worker di servizio aggiornate.

  • Se impostato su 'imports', la cache HTTP non verrà mai consultata durante la ricerca di aggiornamenti allo script /service-worker.js, ma verrà consultata durante il recupero di eventuali script importati (path/to/import.js nel nostro esempio). Si tratta dell'impostazione predefinita e corrisponde al comportamento a partire da Chrome 68.

  • Se impostato su 'all', la cache HTTP verrà consultata quando vengono effettuate richieste sia per lo script /service-worker.js di primo livello sia per eventuali script importati all'interno del worker di servizio, come path/to/import.js. Questa opzione corrisponde al comportamento precedente di Chrome, prima di Chrome 68.

  • Se impostato su 'none', la cache HTTP non verrà consultata quando vengono effettuate richieste per il /service-worker.js di primo livello o per gli script importati, ad esempio l'ipotetico path/to/import.js.

Ad esempio, il seguente codice registra un worker di servizio e garantisce che la cache HTTP non venga mai consultata durante la ricerca di aggiornamenti allo script /service-worker.js o a 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.
  });
}

Controlla la presenza di aggiornamenti agli script importati

Prima di Chrome 78, qualsiasi script di worker di servizio caricato tramite importScripts() viene recuperato una sola volta (controllando prima la cache HTTP o tramite la rete, a seconda della configurazione di updateViaCache). Dopo il recupero iniziale, verrà memorizzato internamente dal browser e non verrà mai recuperato di nuovo.

L'unico modo per forzare un servizio già installato a rilevare le modifiche apportate a un script importato era modificare l'URL dello script, in genere aggiungendo un valore semver (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 i contenuti dello script del servizio di primo livello cambiano, il che a sua volta attiva il flusso di aggiornamento del servizio di lavoro.

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

Se un controllo dell'aggiornamento di uno script importato genera una differenza byte per byte rispetto a quanto memorizzato in precedenza dal servizio worker, verrà attivato il flusso di aggiornamento completo del servizio worker, anche se il file del servizio worker di primo livello rimane invariato.

Il comportamento di Chrome 78 corrisponde a quello implementato da Firefox 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 notare 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é è l'impostazione predefinita per il tuo ambiente di hosting, potresti iniziare a notare un aumento delle richieste HTTP aggiuntive per /service-worker.js effettuate sul tuo server, ovvero richieste che in precedenza venivano soddisfatte dalla cache HTTP. Se vuoi continuare a consentire al valore dell'intestazione Cache-Control di influenzare l'aggiornamento di /service-worker.js, dovrai iniziare a impostare esplicitamente updateViaCache: 'all' quando registri il tuo servizio worker.

Dato che potrebbe esserci un numero elevato di utenti con versioni precedenti del browser, è comunque buona prassi continuare a impostare l'intestazione HTTP Cache-Control: max-age=0 negli script di service worker, anche se i browser più recenti potrebbero ignorarli.

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

Pubblicazione degli script importati

A partire da Chrome 78, gli sviluppatori potrebbero notare un aumento delle richieste HTTP in entrata per le risorse caricate tramite importScripts(), poiché ora verranno controllati gli aggiornamenti.

Se vuoi evitare questo traffico HTTP aggiuntivo, imposta intestazioni Cache-Control a lungo termine quando pubblichi script che includono semver o hash nei loro URL e affidati al comportamento predefinito di updateViaCache di 'imports'.

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

Per approfondire

"Il ciclo di vita dei worker di servizio" e "Best practice per la memorizzazione nella cache e problemi relativi a max-age", entrambi di Jake Archibald, sono letture consigliate per tutti gli sviluppatori che eseguono il deployment di qualsiasi elemento sul web.