Diffuser des mises à jour sur les pages avec des service workers

Andrew Guan
Andrew Guan
Demián Renzulli
Demián Renzulli

Dans certains cas, le service worker peut être amené à communiquer de manière proactive avec l'un des onglets actifs qu'il contrôle pour informer d'un événement donné. Voici quelques exemples :

  • Informer la page lorsqu'une nouvelle version du service worker a été installée afin qu'elle puisse afficher un bouton Mettre à jour pour actualiser à l'utilisateur pour qu'il puisse accéder immédiatement à la nouvelle fonctionnalité.
  • Informez l'utilisateur d'une modification apportée aux données mises en cache côté service worker en affichant une indication, par exemple: "L'application est maintenant prête à fonctionner hors connexion" ou "Nouvelle version du contenu disponible".
Schéma illustrant un service worker qui communique avec la page pour envoyer une mise à jour.

Nous appellerons ces types de cas d'utilisation où le service worker n'a pas besoin de recevoir un message de la page pour lancer une communication "broadcast updates" (mises à jour de diffusion). Dans ce guide, nous allons examiner différentes façons d'implémenter ce type de communication entre les pages et les services workers, à l'aide d'API de navigateur standards et de la bibliothèque Workbox.

Cas de production

Tinder

La PWA Tinder utilise workbox-window pour écouter les moments importants du cycle de vie du service worker à partir de la page ("installé", "contrôlé" et "activé"). Ainsi, lorsqu'un nouveau service worker entre en jeu, il affiche une bannière Mise à jour disponible pour que les utilisateurs puissent actualiser la PWA et accéder aux dernières fonctionnalités:

Capture d'écran de la fonctionnalité "Mise à jour disponible" de la plate-forme Web de Tinder.
Dans la PWA Tinder, le service worker indique à la page qu'une nouvelle version est prête, et la page affiche une bannière "Mise à jour disponible" aux utilisateurs.

Squoosh

Dans la PWA Squish, lorsque le service worker a mis en cache tous les éléments nécessaires pour qu'il fonctionne hors connexion, il envoie un message à la page pour afficher un message "Prêt à fonctionner hors connexion", informant l'utilisateur de cette fonctionnalité:

Capture d'écran de la fonctionnalité "Prêt à fonctionner hors connexion" de l'application Web Squoosh.
Dans la PWA Squoosh, le service worker diffuse une mise à jour de la page lorsque le cache est prêt, et la page affiche le message "Prêt à fonctionner hors connexion".

Utiliser Workbox

Écouter les événements de cycle de vie du service worker

workbox-window fournit une interface simple pour écouter les événements importants du cycle de vie du service worker. Sous le capot, la bibliothèque utilise des API côté client telles que updatefound et statechange, et fournit des écouteurs d'événements de niveau supérieur dans l'objet workbox-window, ce qui permet à l'utilisateur de consommer plus facilement ces événements.

Le code de page suivant vous permet de détecter chaque fois qu'une nouvelle version du service worker est installée afin de la communiquer à l'utilisateur:

const wb = new Workbox('/sw.js');

wb.addEventListener('installed', (event) => {
  if (event.isUpdate) {
    // Show "Update App" banner
  }
});

wb.register();

Informer la page des modifications apportées aux données du cache

Le package Workbox workbox-broadcast-update fournit un moyen standard de signaler aux clients de fenêtre qu'une réponse mise en cache a été mise à jour. Cette approche est généralement utilisée avec la stratégie StaleWhileRevalidate.

Pour diffuser des mises à jour, ajoutez un broadcastUpdate.BroadcastUpdatePlugin à vos options de stratégie côté service worker:

import {registerRoute} from 'workbox-routing';
import {StaleWhileRevalidate} from 'workbox-strategies';
import {BroadcastUpdatePlugin} from 'workbox-broadcast-update';

registerRoute(
  ({url}) => url.pathname.startsWith('/api/'),
  new StaleWhileRevalidate({
    plugins: [
      new BroadcastUpdatePlugin(),
    ],
  })
);

Dans votre application Web, vous pouvez écouter ces événements comme suit:

navigator.serviceWorker.addEventListener('message', async (event) => {
  // Optional: ensure the message came from workbox-broadcast-update
  if (event.data.meta === 'workbox-broadcast-update') {
    const {cacheName, updatedUrl} = event.data.payload;

    // Do something with cacheName and updatedUrl.
    // For example, get the cached content and update
    // the content on the page.
    const cache = await caches.open(cacheName);
    const updatedResponse = await cache.match(updatedUrl);
    const updatedText = await updatedResponse.text();
  }
});

Utiliser les API du navigateur

Si les fonctionnalités fournies par Workbox ne suffisent pas à vos besoins, utilisez les API de navigateur suivantes pour implémenter des mises à jour de diffusion:

API Broadcast Channel

Le service worker crée un objet BroadcastChannel et commence à lui envoyer des messages. Tout contexte (par exemple, une page) intéressé par la réception de ces messages peut instancier un objet BroadcastChannel et implémenter un gestionnaire de messages pour les recevoir.

Pour informer la page lorsqu'un nouveau service worker est installé, utilisez le code suivant:

// Create Broadcast Channel to send messages to the page
const broadcast = new BroadcastChannel('sw-update-channel');

self.addEventListener('install', function (event) {
  // Inform the page every time a new service worker is installed
  broadcast.postMessage({type: 'CRITICAL_SW_UPDATE'});
});

La page écoute ces événements en s'abonnant à sw-update-channel:

// Create Broadcast Channel and listen to messages sent to it
const broadcast = new BroadcastChannel('sw-update-channel');

broadcast.onmessage = (event) => {
  if (event.data && event.data.type === 'CRITICAL_SW_UPDATE') {
    // Show "update to refresh" banner to the user.
  }
};

Il s'agit d'une technique simple, mais sa limite est la compatibilité avec les navigateurs: au moment de la rédaction de cet article, Safari n'est pas compatible avec cette API.

API client

L'API client offre un moyen simple de communiquer avec plusieurs clients à partir du service worker en itérant sur un tableau d'objets Client.

Utilisez le code de service worker suivant pour envoyer un message au dernier onglet sélectionné:

// Obtain an array of Window client objects
self.clients.matchAll(options).then(function (clients) {
  if (clients && clients.length) {
    // Respond to last focused tab
    clients[0].postMessage({type: 'MSG_ID'});
  }
});

La page implémente un gestionnaire de messages pour intercepter ces messages:

// Listen to messages
navigator.serviceWorker.onmessage = (event) => {
     if (event.data && event.data.type === 'MSG_ID') {
         // Process response
   }
};

L'API cliente est une excellente option pour diffuser des informations sur plusieurs onglets actifs, par exemple. L'API est compatible avec tous les principaux navigateurs, mais pas toutes ses méthodes. Vérifiez la compatibilité du navigateur avant de l'utiliser.

Canal de messagerie

Le canal de messages nécessite une étape de configuration initiale, en transmettant un port de la page au service worker, afin d'établir un canal de communication entre eux. La page instancie un objet MessageChannel et transmet un port au service worker via l'interface postMessage():

const messageChannel = new MessageChannel();

// Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
  messageChannel.port2,
]);

La page écoute les messages en implémentant un gestionnaire "onmessage" sur ce port:

// Listen to messages
messageChannel.port1.onmessage = (event) => {
  // Process message
};

Le service worker reçoit le port et en enregistre une référence:

// Initialize
let communicationPort;

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'PORT_INITIALIZATION') {
    communicationPort = event.ports[0];
  }
});

À partir de là, il peut envoyer des messages à la page en appelant postMessage() dans la référence au port:

// Communicate
communicationPort.postMessage({type: 'MSG_ID' });

MessageChannel peut être plus complexe à implémenter, car il est nécessaire d'initialiser les ports, mais il est compatible avec tous les principaux navigateurs.

Étapes suivantes

Dans ce guide, nous avons exploré un cas particulier de communication entre le navigateur et le service worker : les mises à jour de diffusion. Les exemples explorés incluent l'écoute d'événements de cycle de vie importants du service worker et la communication à la page des modifications apportées au contenu ou aux données mises en cache. Vous pouvez imaginer des cas d'utilisation plus intéressants où le service worker communique de manière proactive avec la page, sans avoir reçu de message au préalable.

Pour en savoir plus sur les modèles de communication entre les fenêtres et les service workers, consultez les ressources suivantes:

  • Guide de mise en cache impérative: appel d'un service worker à partir de la page pour mettre en cache les ressources à l'avance (par exemple, dans les scénarios de préchargement).
  • Communication bidirectionnelle: délégation d'une tâche à un service worker (par exemple, un téléchargement lourd) et mise à jour de la page sur la progression.

Ressources supplémentaires