Gestione immediata degli aggiornamenti dei service worker

Per impostazione predefinita, il ciclo di vita del service worker richiede che quando viene trovato e installato un service worker aggiornato, tutte le schede aperte che il service worker attuale sta controllando devono essere chiuse o sottoposte a navigazione prima che il service worker aggiornato venga attivato e assuma il controllo.

In molti casi, può andar bene consentire che ciò avvenga a tempo debito, ma in alcuni casi potrebbe essere opportuno avvisare l'utente che c'è un aggiornamento del service worker in attesa e poi automatizzare il processo di passaggio al nuovo service worker. Per farlo, dovrai aggiungere del codice nella tua pagina e il tuo service worker.

Il codice da inserire nella pagina

Il codice seguente viene eseguito in un elemento <script> incorporato utilizzando moduli JavaScript importati da una versione di workbox-window ospitata da CDN. Registra un service worker utilizzando workbox-window e reagisce se il service worker rimane bloccato nella fase di attesa. Quando viene trovato un service worker in attesa, questo codice informa l'utente che è disponibile una versione aggiornata del sito e lo invita a ricaricarlo.

<!-- This script tag uses JavaScript modules, so the proper `type` attribute value is required -->
<script type="module">
  // This code sample uses features introduced in Workbox v6.
  import {Workbox} from 'https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-window.prod.mjs';

  if ('serviceWorker' in navigator) {
    const wb = new Workbox('/sw.js');
    let registration;

    const showSkipWaitingPrompt = async (event) => {
      // Assuming the user accepted the update, set up a listener
      // that will reload the page as soon as the previously waiting
      // service worker has taken control.
      wb.addEventListener('controlling', () => {
        // At this point, reloading will ensure that the current
        // tab is loaded under the control of the new service worker.
        // Depending on your web app, you may want to auto-save or
        // persist transient state before triggering the reload.
        window.location.reload();
      });

      // When `event.wasWaitingBeforeRegister` is true, a previously
      // updated service worker is still waiting.
      // You may want to customize the UI prompt accordingly.

      // This code assumes your app has a promptForUpdate() method,
      // which returns true if the user wants to update.
      // Implementing this is app-specific; some examples are:
      // https://open-ui.org/components/alert.research or
      // https://open-ui.org/components/toast.research
      const updateAccepted = await promptForUpdate();

      if (updateAccepted) {
        wb.messageSkipWaiting();
      }
    };

    // Add an event listener to detect when the registered
    // service worker has installed but is waiting to activate.
    wb.addEventListener('waiting', (event) => {
      showSkipWaitingPrompt(event);
    });

    wb.register();
  }
</script>

Se accetta, messageSkipWaiting() chiede al service worker in attesa di richiamare self.skipWaiting(), il che significa che inizierà l'attivazione. Una volta attivato, il nuovo service worker assumerà il controllo di tutti i client esistenti, attivando l'evento controlling in workbox-window. In questo caso, la pagina corrente viene ricaricata utilizzando l'ultima versione di tutti gli asset pre-memorizzati nella cache e l'eventuale nuova logica di routing trovata nel service worker aggiornato.

Il codice da inserire nel service worker

Una volta ricevuto il codice dalla sezione precedente della pagina, dovrai aggiungere del codice al service worker per fargli sapere quando saltare la fase di attesa. Se utilizzi generateSW da workbox-build e hai impostato l'opzione skipWaiting su false (impostazione predefinita), non devi fare altro, perché il codice seguente verrà incluso automaticamente nel file del service worker generato.

Se stai scrivendo un service worker, magari insieme a uno degli strumenti di creazione di Workbox in modalità injectManifest, dovrai aggiungere tu stesso il codice seguente:

addEventListener('message', (event) => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
});

Ascolterà i messaggi inviati al service worker da workbox-window con un valore di type pari a SKIP_WAITING e, in questo caso, chiama self.skipWaiting(). Il metodo messageSkipWaiting() in workbox-window, mostrato nell'esempio di codice precedente, è responsabile dell'invio di questo messaggio.

Hai bisogno di mostrare un prompt?

Questo non è un pattern che deve seguire tutte le applicazioni di cui viene eseguito il deployment di un service worker. È per alcuni scenari in cui non offrire l'opportunità di ricaricare una pagina durante l'aggiornamento di un service worker può causare comportamenti imprevisti. Non esistono regole ferree che stabiliscono se mostrare o meno una richiesta di ricaricamento, ma ecco alcune situazioni in cui può essere utile:

  • Fai uso intensivo della prememorizzazione nella cache. Per quanto riguarda gli asset statici, potresti riscontrare problemi in un secondo momento se utilizzi una strategia network-first o solo rete per le richieste di navigazione, ma il caricamento lento delle risorse statiche. Ciò può causare situazioni in cui gli asset con controllo delle versioni possono cambiare e un service worker non li ha pre-memorizzati nella cache. L'offerta di un pulsante di ricaricamento qui potrebbe evitare alcuni comportamenti imprevisti.
  • Se pubblichi codice HTML prememorizzato nella cache, In questo caso, dovresti vivamente offrire un pulsante Ricarica per gli aggiornamenti del service worker, poiché gli aggiornamenti al codice HTML non verranno riconosciuti finché il service worker aggiornato non prende il controllo.
  • Se non fai affidamento principalmente sulla memorizzazione nella cache di runtime. Quando memorizzi le risorse nella cache in fase di runtime, non devi comunicare all'utente che deve ricaricarsi. Quando gli asset sottoposti al controllo delle versioni cambiano, verranno aggiunti alla cache di runtime a tempo debito, supponendo che le richieste di navigazione utilizzino una strategia network-first o solo rete.
  • Quando utilizzi una strategia inattiva-ment-revalidate, puoi prendere in considerazione l'utilizzo del modulo workbox-broadcast-update per notificare agli utenti gli aggiornamenti del service worker.

La necessità di informare l'utente degli aggiornamenti a un service worker dipende dalla tua applicazione e dai suoi requisiti specifici. Se noti che i tuoi utenti riscontrano comportamenti strani quando esegui il push di un nuovo service worker, questo è probabilmente il segnale migliore che dovresti avvisare.