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

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

Dans de nombreux cas, il peut être judicieux d'autoriser cette modification. Toutefois, dans certains cas, vous pouvez prévenir l'utilisateur qu'une mise à jour du service worker est en attente, puis automatiser le processus de basculement 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 sur CDN. Il enregistre un service worker à l'aide de workbox-window et réagit s'il se bloque dans la phase d'attente. Lorsqu'un service worker en attente est détecté, 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'ils acceptent, messageSkipWaiting() indique au service worker en attente d'appeler self.skipWaiting(), ce qui signifie qu'il commence à s'activer. Une fois activé, le nouveau service worker prendra le contrôle de tous les clients existants, déclenchant ainsi l'événement controlling dans workbox-window. Dans ce cas, la page actuelle est actualisée à l'aide de la dernière version de tous les éléments mis en cache et de la nouvelle logique de routage détectée dans le service worker mis à jour.

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 afin qu'il sache 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, car le code ci-dessous sera automatiquement inclus dans le fichier de service worker généré.

Si vous écrivez votre propre service worker, éventuellement avec 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();
  }
});

Celui-ci écoutera les messages envoyés au service worker depuis workbox-window avec une valeur type définie sur SKIP_WAITING et, le cas échéant, appelle self.skipWaiting(). La méthode messageSkipWaiting() dans workbox-window, illustré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 utilisée dans certains cas où l'impossibilité d'actualiser une page lors d'une mise à jour d'un service worker peut entraîner des comportements inattendus. Il n'existe pas de règles strictes pour afficher une invite d'actualisation, mais voici quelques situations dans lesquelles cela peut être judicieux:

  • Vous utilisez beaucoup la mise en cache préalable. En ce qui concerne les éléments statiques, vous risquez de rencontrer des problèmes par la suite si vous utilisez une stratégie axée sur le réseau ou uniquement le réseau pour les demandes de navigation, mais avec le chargement différé des éléments statiques. Il se peut donc que des éléments avec gestion des versions changent, alors qu'un service worker ne les a pas mis en pré-cache. Fournir un bouton d'actualisation ici peut permettre d'éviter certains comportements inattendus.
  • Si vous diffusez du contenu HTML mis en pré-cache Dans ce cas, vous devriez envisager fortement de proposer un bouton d'actualisation lors des mises à jour d'un service worker, car les mises à jour du code HTML ne sont pas reconnues tant que le service worker mis à jour ne prend pas le contrôle.
  • Si vous n'utilisez pas principalement la mise en cache de l'environnement d'exécution. Lorsque vous mettez en cache des ressources au moment de l'exécution, vous n'avez pas besoin d'informer l'utilisateur qu'il doit actualiser. À mesure que les éléments avec gestion des versions changent, ils sont ajoutés au cache de l'environnement d'exécution en temps voulu, à condition que les requêtes de navigation utilisent une stratégie axée sur le réseau ou uniquement sur le réseau.
  • Lorsque vous utilisez une stratégie stale-while-revalidate, vous pouvez envisager d'utiliser le module workbox-broadcast-update pour avertir 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 spécifiques. Si vous constatez que vos utilisateurs subissent des comportements étranges lorsque vous déployez un nouveau service worker, nous vous recommandons de les en avertir.