Gestion immédiate des mises à jour effectuées par un service worker

Par défaut, le cycle de vie du service worker implique que lorsqu'un service worker mis à jour est détecté et installé, tous les onglets ouverts qu'il contrôle actuellement doivent être fermés ou faire l'objet d'une navigation avant que le service worker mis à jour ne s'active et prenne le contrôle.

Dans de nombreux cas, il peut être acceptable de laisser cela se produire en temps voulu, mais dans certains cas, vous pouvez informer l'utilisateur qu'une mise à jour du service worker est en attente, puis automatiser le processus de transition vers le nouveau service worker. Pour ce faire, vous devez ajouter du code à votre page et votre service worker.

Le code à insérer dans votre page

Le code suivant s'exécute dans un élément <script> intégré à l'aide de modules JavaScript importés à partir d'une version de workbox-window hébergée par CDN. Elle enregistre un service worker à l'aide de workbox-window et réagira s'il se retrouve bloqué dans la phase d'attente. Lorsqu'un service worker en attente est trouvé, ce code informe l'utilisateur qu'une version mise à jour du site est disponible et l'invite à actualiser la page.

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

S'il accepte, messageSkipWaiting() indique au service worker en attente d'appeler self.skipWaiting(), ce qui signifie qu'il commencera à s'activer. Une fois activé, le nouveau service worker prendra le contrôle des clients existants, ce qui déclenchera l'événement controlling dans workbox-window. Lorsque cela se produit, la page actuelle est actualisée à l'aide de la dernière version de tous les éléments mis en pré-cache et de toute nouvelle logique de routage trouvée dans le service worker mis à jour.

Le code à insérer dans votre service worker

Une fois que vous avez récupéré le code de la section précédente sur votre page, vous devez ajouter du code à votre service worker pour lui indiquer quand ignorer la phase d'attente. Si vous utilisez generateSW depuis workbox-build et que l'option skipWaiting est définie sur false (valeur par défaut), vous n'avez rien d'autre à faire. En effet, le code ci-dessous sera automatiquement inclus dans le fichier service worker généré.

Si vous écrivez votre propre service worker (en même temps que l'un des outils de compilation de Workbox en mode injectManifest), vous devez ajouter vous-même le code suivant:

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

Ce paramètre écoute les messages envoyés au service worker depuis workbox-window avec une valeur type définie sur SKIP_WAITING, puis appelle self.skipWaiting(). La méthode messageSkipWaiting() dans workbox-window, présentée dans l'exemple de code précédent, est responsable de l'envoi de ce message.

Avez-vous besoin d'afficher une invite ?

Il ne s'agit pas d'un modèle que toutes les applications qui déploient un service worker doivent suivre. Elle est destinée aux scénarios dans lesquels l'absence de possibilité d'actualisation d'une page lors de la mise à jour d'un service worker peut entraîner des comportements inattendus. Il n'existe aucune règle stricte pour déterminer si une invite d'actualisation doit être affichée, mais dans certaines situations, cela peut être judicieux:

  • Vous utilisez beaucoup la mise en cache précoce. En ce qui concerne les éléments statiques, vous risquez de rencontrer des problèmes par la suite si vous utilisez une stratégie avec priorité au réseau ou une stratégie axée uniquement sur le réseau pour les requêtes de navigation, mais avec le chargement différé des éléments statiques. Cela peut entraîner des modifications des éléments avec versions gérées, lorsqu'un service worker ne les a pas mis en cache en amont. Ajouter un bouton d'actualisation ici peut éviter certains comportements inattendus.
  • Si vous diffusez du code HTML en pré-cache Dans ce cas, nous vous conseillons vivement de proposer un bouton d'actualisation sur les mises à jour du service worker, car les mises à jour de ce code HTML ne seront pas reconnues tant que le service worker mis à jour ne prendra pas le contrôle.
  • Si vous ne comptez pas principalement sur la mise en cache de l'environnement d'exécution. Lors de la mise en cache des ressources au moment de l'exécution, il n'est pas nécessaire d'indiquer à l'utilisateur qu'il doit actualiser la page. À mesure que les éléments avec des versions gérées changent, ils seront ajoutés au cache d'exécution en temps voulu, en supposant que les requêtes de navigation utilisent une stratégie axée sur le réseau ou sur le réseau uniquement.
  • Lorsque vous utilisez une stratégie stale-while-revalidate, vous pouvez envisager d'utiliser le module workbox-broadcast-update pour informer les utilisateurs des mises à jour des service workers.

La nécessité ou non d'informer l'utilisateur des mises à jour d'un service worker dépend de votre application et de ses exigences uniques. Si vous constatez que vos utilisateurs ont des comportements étranges lorsque vous repoussez un nouveau service worker, le meilleur moyen de les avertir est probablement de les avertir.