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".
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:
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é:
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.