Maneja las actualizaciones de service worker de inmediato

De forma predeterminada, el ciclo de vida del service worker exige que, cuando se encuentre e instale un service worker actualizado, todas las pestañas abiertas que controla el service worker actual deben cerrarse o someterse a una navegación antes de que se active y tome el control del service worker actualizado.

En muchos casos, puede estar bien permitir que esto suceda a su debido tiempo, pero, en algunos casos, es posible que desees avisar al usuario que hay una actualización pendiente del service worker y, luego, automatizar el proceso de cambio al nuevo service worker. Para ello, tendrás que agregar código a tu página y a tu service worker.

El código que debes colocar en tu página

El siguiente código se ejecuta en un elemento <script> intercalado mediante módulos de JavaScript importados de una versión de workbox-window alojada en CDN. Registra un service worker usando workbox-window y reaccionará si el service worker se bloquea en la fase de espera. Cuando se encuentra un service worker en espera, este código le informa al usuario que hay una versión actualizada del sitio disponible y le solicita que vuelva a cargar la página.

<!-- 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>

Si acepta, messageSkipWaiting() le indica al service worker que está en espera que invoque self.skipWaiting(), lo que significa que comenzará a activarse. Una vez activado, el nuevo service worker tomará el control de los clientes existentes, lo que activará el evento controlling en workbox-window. Cuando esto sucede, la página actual se vuelve a cargar con la última versión de todos los recursos previamente almacenados en caché y cualquier lógica de enrutamiento nueva que se encuentre en el service worker actualizado.

El código que debes colocar en tu service worker

Una vez que hayas obtenido el código de la sección anterior en tu página, deberás agregar código al service worker para que sepa cuándo debe omitir la fase de espera. Si usas generateSW de workbox-build y tienes la opción skipWaiting establecida en false (la configuración predeterminada), no tienes que hacer nada, ya que el siguiente código se incluirá automáticamente en el archivo de service worker generado.

Si escribes tu propio service worker, quizás junto con una de las herramientas de compilación de Workbox en el modo injectManifest, deberás agregar el siguiente código:

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

De esta manera, se detectarán los mensajes enviados al service worker desde workbox-window con un valor type de SKIP_WAITING y, cuando eso suceda, se llamará a self.skipWaiting(). El método messageSkipWaiting() en workbox-window, que se muestra en la muestra de código anterior, es responsable de enviar este mensaje.

¿Necesitas mostrar un mensaje?

Este no es un patrón que deben seguir todas las aplicaciones que implementan un service worker. Es para situaciones selectas en las que no proporcionar la oportunidad de volver a cargar una página en una actualización del service worker puede provocar comportamientos inesperados. No hay reglas estrictas sobre si debes mostrar un mensaje de recarga, pero estas son algunas situaciones en las que podría ser útil:

  • El almacenamiento previo en caché se usa mucho. En lo que concierne a los recursos estáticos, puedes tener problemas más adelante si usas una estrategia centrada en la red o solo en la red para las solicitudes de navegación, pero con recursos estáticos de carga diferida. Esto puede provocar situaciones en las que los elementos con control de versiones pueden cambiar y un service worker no los ha almacenado previamente en caché. Ofrecer un botón de recarga aquí puede evitar algunos comportamientos inesperados.
  • Si entregas código HTML previamente almacenado en caché En este caso, deberías considerar especialmente ofrecer un botón de volver a cargar en las actualizaciones del service worker, ya que las actualizaciones de ese HTML no se reconocerán hasta que el service worker actualizado tome el control.
  • Si no dependes más del almacenamiento en caché en el entorno de ejecución Cuando almacenas recursos en caché durante el tiempo de ejecución, no es necesario avisarle al usuario que debe volver a cargar la página. A medida que cambien los recursos con control de versiones, se agregarán a la caché del entorno de ejecución a su debido tiempo, siempre que las solicitudes de navegación usen una estrategia centrada en la red o solo en la red.
  • Cuando uses una estrategia de inactividad durante la revalidación, te recomendamos usar el módulo workbox-broadcast-update para notificar a los usuarios sobre las actualizaciones del service worker.

La decisión de notificar al usuario sobre las actualizaciones de un service worker depende de tu aplicación y sus requisitos únicos. Si descubres que tus usuarios experimentan comportamientos extraños cuando envías un nuevo service worker, esa es probablemente la mejor señal de que debes notificarlos.