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 l'attuale service worker sta controllando devono essere chiuse o sottoposte a una navigazione prima che il service worker aggiornato si attivi e assuma il controllo.

In molti casi, potrebbe essere opportuno consentire che ciò avvenga a tempo debito, ma in alcuni casi potresti voler avvisare l'utente che c'è un aggiornamento del service worker in sospeso e quindi automatizzare il processo di passaggio al nuovo service worker. Per farlo, dovrai aggiungere del codice nella pagina e il Service worker.

Il codice da inserire nella pagina

Il seguente codice viene eseguito in un elemento <script> incorporato utilizzando i moduli JavaScript importati da una versione di workbox-window ospitata da una rete CDN. Registra un service worker utilizzando workbox-window e reagirà 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 gli chiede di ricaricare.

<!-- 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 accettano, messageSkipWaiting() dice 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 prememorizzati nella cache e qualsiasi nuova logica di routing trovata nel service worker aggiornato.

Il codice da inserire nel service worker

Una volta ottenuto il codice della sezione precedente della pagina, devi aggiungere al Service worker un po' di codice che gli consenta di sapere quando saltare la fase di attesa. Se utilizzi generateSW di workbox-build e l'opzione skipWaiting è impostata su false (valore predefinito), non devi fare nient'altro: il codice seguente verrà incluso automaticamente nel file del service worker generato.

Se stai scrivendo da solo un service worker, forse insieme a uno degli strumenti di creazione di Workbox in modalità injectManifest, dovrai aggiungere personalmente il codice seguente:

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

Questa operazione ascolta i messaggi inviati al service worker da workbox-window con un valore type di 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 ogni applicazione che esegue il deployment di un service worker deve seguire. È per scenari selezionati in cui non riuscire a fornire l'opportunità di ricaricare una pagina in un aggiornamento del service worker potrebbe causare comportamenti imprevisti. Non esistono regole fisse per stabilire se mostrare o meno un prompt di ricaricamento, ma ecco alcune situazioni in cui potrebbe essere opportuno:

  • Usi la pre-memorizzazione 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 con il caricamento lento delle risorse statiche. Ciò può causare situazioni in cui gli asset sottoposti al controllo delle versioni possono cambiare e un service worker non li ha pre-memorizzati nella cache. Se offri un pulsante di ricaricamento qui, potresti evitare alcuni comportamenti imprevisti.
  • Se pubblichi HTML prememorizzati nella cache. In questo caso dovresti vivere l'idea di offrire un pulsante di ricaricamento sugli aggiornamenti dei service worker, poiché gli aggiornamenti a quell'HTML non vengono riconosciuti finché il service worker aggiornato non prende il controllo.
  • Se non fai affidamento principalmente sulla memorizzazione nella cache del runtime. Quando si memorizza nella cache le risorse in fase di runtime, non è necessario comunicare all'utente che deve ricaricare. Quando gli asset con versione cambiano, gli asset vengono aggiunti alla cache di runtime a tempo debito, assumendo che le richieste di navigazione utilizzino una strategia basata sulla rete o solo di rete.
  • Quando utilizzi una strategia stale-while-revalidate, potresti prendere in considerazione l'utilizzo del modulo workbox-broadcast-update per informare gli utenti degli aggiornamenti dei Service worker.

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