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