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 avoir besoin de communiquer de manière proactive avec l'un des onglets actifs qu'il contrôle pour l'informer d'un événement spécifique. 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" permettant à l'utilisateur d'accéder immédiatement à la nouvelle fonctionnalité.
  • Informer l'utilisateur d'une modification des données mises en cache qui a eu lieu côté service worker, en affichant une indication telle que : "L'application est désormais prête à fonctionner hors connexion" ou "Nouvelle version du contenu disponible".
Schéma montrant un service worker communiquant avec la page pour envoyer une mise à jour.

Nous appellerons ces types de cas d'utilisation dans lesquels le service worker n'a pas besoin de recevoir de message de la page pour démarrer une communication "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 service 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" afin que l'utilisateur puisse actualiser la PWA et accéder aux dernières fonctionnalités :

Capture d'écran de la fonctionnalité "Mise à jour disponible" de l'application Web 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".

Squoosh

Dans la PWA Squoosh, lorsque le service worker a mis en cache tous les éléments nécessaires pour qu'elle fonctionne hors connexion, il envoie un message à la page pour afficher une notification "Prêt à fonctionner hors connexion" , informant l'utilisateur de la 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 sur la page lorsque le cache est prêt, et la page affiche la notification "Prêt à fonctionner hors connexion".

Utiliser Workbox

Écouter les événements du 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. En arrière-plan, 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 pouvoir 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 mises en cache

Le package Workbox workbox-broadcast-update fournit un moyen standard d'informer les clients de la fenêtre qu'une réponse mise en cache a été mise à jour. Il est le plus souvent utilisé avec la stratégie StaleWhileRevalidate.

Pour diffuser des mises à jour, ajoutez un broadcastUpdate.BroadcastUpdatePlugin aux options de votre 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 des API de navigateur

Si la fonctionnalité fournie par Workbox ne suffit pas à vos besoins, utilisez les API de navigateur suivantes pour implémenter "mises à jour de diffusion" :

API Broadcast Channel

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

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 limitation est la prise en charge des navigateurs : au moment de la rédaction de cet article, Safari n'est pas compatible avec cette API.

API Client

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

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 Client est une excellente option pour les cas comme la diffusion d'informations sur plusieurs onglets actifs. 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 messagerie 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 enregistre une référence à celui-ci :

// Initialize
let communicationPort;

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

À partir de ce moment, 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 en raison de la nécessité 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 une fenêtre et un service worker : "mises à jour de diffusion". Les exemples explorés incluent l'écoute des événements importants du cycle de vie du service worker et la communication à la page des modifications apportées au contenu ou aux données mises en cache. Vous pouvez imaginer d'autres cas d'utilisation intéressants dans lesquels le service worker communique de manière proactive avec la page, sans recevoir de message auparavant.

Pour en savoir plus sur les modèles de communication entre une fenêtre et un service worker, consultez les ressources suivantes :

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

Ressources supplémentaires