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 el service worker actual controle se cierren o se sometan a una navegación antes de que el service worker actualizado se active y tome el control.

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

El código que debe colocar en su página

El siguiente código se ejecuta en un elemento <script> intercalado con módulos de JavaScript importados de una versión de workbox-window alojada en CDN. Registra un service worker con workbox-window y reaccionará si este se bloquea en la fase de espera. Cuando se encuentra un service worker en espera, este código informa al usuario que hay una versión actualizada del sitio disponible y le pide 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 aceptan, messageSkipWaiting() le indica al service worker en espera que invoque self.skipWaiting(), lo que significa que comenzará a activarse. Una vez que se active, el service worker nuevo tomará el control de los clientes existentes y activará el evento controlling en workbox-window. Cuando esto sucede, la página actual se vuelve a cargar utilizando la última versión de todos los elementos almacenados previamente en caché y cualquier lógica de enrutamiento nueva que se encuentre en el service worker actualizado.

El código que debes poner en el service worker

Una vez que tengas el código de la sección anterior de tu página, deberás agregar un código a tu service worker que le indique cuándo omitir la fase de espera. Si usas generateSW de workbox-build y la opción skipWaiting está establecida en false (la opción predeterminada), no necesitas hacer nada más, ya que el siguiente código se incluirá automáticamente en el archivo de service worker generado.

Si escribes tu propio service worker, tal vez junto con una de las herramientas de compilación de Workbox en modo injectManifest, tendrás que agregar el siguiente código tú mismo:

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

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

¿Necesitas mostrar un mensaje?

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

  • Usas mucho el almacenamiento en caché. En cuanto a los recursos estáticos, puedes tener problemas más adelante si usas una estrategia centrada en la red o solo de red para las solicitudes de navegación, pero con recursos estáticos de carga diferida. Esto puede causar situaciones en las que los elementos con versión pueden cambiar y un service worker no los haya almacenado previamente en caché. Si ofreces un botón de recarga aquí, es posible que se eviten algunos comportamientos inesperados.
  • Si estás entregando HTML prealmacenado 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 principalmente del almacenamiento en caché del entorno de ejecución Cuando almacenas los recursos en caché durante el tiempo de ejecución, no necesitas informar al usuario que debe volver a cargarlos. 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, suponiendo que las solicitudes de navegación usen una estrategia centrada en la red o solo en la red.
  • Cuando uses una estrategia stale-while-revalidate, te recomendamos usar el módulo workbox-broadcast-update para notificar a los usuarios sobre las actualizaciones del service worker.

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