Communication bidirectionnelle avec les service workers

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

Dans certains cas, une application Web peut avoir besoin d'établir un canal de communication bidirectionnel entre les et le service worker.

Par exemple, dans une PWA de podcast, vous pouvez créer une fonctionnalité permettant à l'utilisateur de télécharger des épisodes pour la consommation hors connexion et de permettre un service worker d'informer régulièrement la page de la progression, de sorte que le principal thread peut mettre à jour l'UI.

Dans ce guide, nous allons explorer les différentes manières de mettre en place une communication bidirectionnelle entre Window et service de nœud de calcul, en explorant différentes API, la bibliothèque Workbox, ainsi que certains cas avancés.

Schéma illustrant un service worker et la page qui échange des messages.

Utiliser Workbox

workbox-window est un ensemble de de la bibliothèque Workbox destinés à s'exécuter dans le contexte de la fenêtre. Le Workbox fournit une méthode messageSW() pour envoyer un message au service worker enregistré de l'instance et attendent une réponse.

Le code de page suivant crée une instance Workbox et envoie un message au service worker pour obtenir sa version:

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

const swVersion = await wb.messageSW({type: 'GET_VERSION'});
console.log('Service Worker version:', swVersion);

Le service worker implémente un écouteur de messages à l'autre bout et répond aux requêtes service worker:

const SW_VERSION = '1.0.0';

self.addEventListener('message', (event) => {
  if (event.data.type === 'GET_VERSION') {
    event.ports[0].postMessage(SW_VERSION);
  }
});

La bibliothèque utilise en arrière-plan une API de navigateur que nous examinerons dans la section suivante: Message Canal, mais en élimine de nombreux les détails de l'implémentation, ce qui facilite son utilisation, tout en exploitant le grand navigateur cette API.

Schéma illustrant la communication bidirectionnelle entre Page Worker et le service worker à l'aide de la fenêtre Workbox

Utiliser des API de navigateur

Si la bibliothèque Workbox ne répond pas à vos besoins, plusieurs API de niveau inférieur sont disponibles pour mettre en place une communication bidirectionnelle entre les pages et les service workers ; Ils présentent des similitudes et leurs différences:

Similitudes :

  • Dans tous les cas, la communication commence d'une extrémité via l'interface postMessage() et est reçue. de l'autre côté en implémentant un gestionnaire message.
  • En pratique, toutes les API disponibles nous permettent d'implémenter les mêmes cas d'utilisation, mais certains d'entre eux peut simplifier le développement dans certains cas.

Différences :

  • Ils ont différentes manières d'identifier l'autre côté de la communication: certains d'entre eux utilisent un référence explicite à l'autre contexte, tandis que d'autres peuvent communiquer implicitement via un proxy instancié de chaque côté.
  • Les navigateurs compatibles varient d'un navigateur à l'autre.
Schéma illustrant la communication bidirectionnelle entre Page Worker et Service Worker, et les API de navigateur disponibles.

API Broadcast Channel

Navigateurs pris en charge

  • Chrome: 54
  • Edge: 79
  • Firefox: 38
  • Safari: 15.4.

Source

L'API Broadcast Channel permet une communication de base entre différents contextes de navigation via BroadcastChannel des objets.

Pour l'implémenter, chaque contexte doit d'abord instancier un objet BroadcastChannel avec le même ID. pour envoyer et recevoir des messages:

const broadcast = new BroadcastChannel('channel-123');

L'objet BroadcastChannel expose une interface postMessage() pour envoyer un message à toute audience contexte:

//send message
broadcast.postMessage({ type: 'MSG_ID', });

Tout contexte de navigateur peut écouter des messages via la méthode onmessage de BroadcastChannel. objet:

//listen to messages
broadcast.onmessage = (event) => {
  if (event.data && event.data.type === 'MSG_ID') {
    //process message...
  }
};

Comme nous l'avons vu, il n'existe pas de référence explicite à un contexte particulier. Il n'est donc pas nécessaire d'obtenir fait référence au service worker ou à un client spécifique.

Schéma illustrant la communication bidirectionnelle entre Page Worker et le service worker, à l'aide d'un objet Broadcast Channel

L'inconvénient est qu'au moment de la rédaction de cet article, l'API est compatible avec Chrome, Firefox et Edge, mais d'autres navigateurs, comme Safari, ne le prennent pas en charge pour l'instant.

API cliente

Navigateurs pris en charge

  • Chrome: 40
  • Edge: 17
  • Firefox: 44
  • Safari: 11.1.

Source

L'API Client vous permet d'obtenir un une référence à tous les objets WindowClient représentant les onglets actifs contrôlés par le service worker ;

Étant donné que la page est contrôlée par un seul service worker, elle écoute et envoie des messages au service worker actif directement via l'interface serviceWorker:

//send message
navigator.serviceWorker.controller.postMessage({
  type: 'MSG_ID',
});

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

De même, le service worker écoute les messages en implémentant un écouteur onmessage:

//listen to messages
self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'MSG_ID') {
    //Process message
  }
});

Pour communiquer en retour avec l'un de ses clients, le service worker obtient un tableau des des objets WindowClient en exécutant des méthodes telles que Clients.matchAll() et Clients.get() Il peut ensuite postMessage() l'un d'entre eux:

//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'});
  }
});
Schéma illustrant un service worker communiquant avec un tableau de clients

Client API est une bonne option pour communiquer facilement avec tous les onglets actifs d'un service worker de manière relativement simple. L'API est compatible avec tous les principaux navigateurs, mais il se peut que toutes ses méthodes ne soient pas disponibles. Vérifiez donc la compatibilité du navigateur avant sur votre site.

Canal de messagerie

Navigateurs pris en charge

  • Chrome: 2.
  • Edge: 12
  • Firefox: 41
  • Safari: 5.

Source

Message Channel requiert définir et transmettre un port d'un contexte à un autre pour établir une communication bidirectionnelle canal.

Pour initialiser le canal, la page instancie un objet MessageChannel et l'utilise pour envoyer un port au service worker enregistré. La page implémente également un écouteur onmessage sur pour qu'il reçoive les messages provenant de l'autre contexte:

const messageChannel = new MessageChannel();

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

//Listen to messages
messageChannel.port1.onmessage = (event) => {
  // Process message
};
Schéma illustrant une page transmettant un port à un service worker pour établir une communication bidirectionnelle.

Le service worker reçoit le port, enregistre une référence à celui-ci et l'utilise pour envoyer un message à l'autre côté:

let communicationPort;

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

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

MessageChannel est actuellement pris en charge par tous les principaux navigateurs.

API avancées: synchronisation et récupération en arrière-plan

Dans ce guide, nous avons découvert comment mettre en œuvre des techniques de communication à double sens, pour des les cas simples, comme la transmission d'un message de chaîne décrivant l'opération à effectuer ou d'une liste d'URL à mettre en cache d'un contexte à l'autre. Dans cette section, nous allons examiner deux API pour gérer des manque de connectivité et longs téléchargements.

Synchronisation en arrière-plan

Navigateurs pris en charge

  • Chrome: 49
  • Edge: 79
  • Firefox: non compatible.
  • Safari: non compatible.

Source

Une application de chat peut vouloir s'assurer que les messages ne sont jamais perdus en raison d'une mauvaise connectivité. La L'API Background Sync vous permet reporter les actions à relancer lorsque l'utilisateur dispose d'une connectivité stable. Cela est utile pour s’assurer que tout ce que l'utilisateur veut envoyer, est réellement envoyé.

Au lieu de l'interface postMessage(), la page enregistre un sync:

navigator.serviceWorker.ready.then(function (swRegistration) {
  return swRegistration.sync.register('myFirstSync');
});

Le service worker écoute ensuite l'événement sync pour traiter le message:

self.addEventListener('sync', function (event) {
  if (event.tag == 'myFirstSync') {
    event.waitUntil(doSomeStuff());
  }
});

La fonction doSomeStuff() doit renvoyer une promesse indiquant la réussite ou l'échec de l'opération ce que nous essayons de faire. Si c'est le cas, la synchronisation est terminée. En cas d'échec, une autre synchronisation est programmée pour réessayez. Les nouvelles tentatives de synchronisation attendent également la connectivité et appliquent un intervalle exponentiel entre les tentatives.

Une fois l'opération terminée, le service worker peut à nouveau communiquer avec la page pour mettre à jour l'UI à l'aide de l'une des API de communication abordées précédemment.

La recherche Google utilise la synchronisation en arrière-plan pour conserver les requêtes ayant échoué en raison d'une mauvaise connectivité, et réessayer plus tard lorsque l'utilisateur sera en ligne. Une fois l'opération terminée, ils communiquent le résultat l'utilisateur via une notification push Web:

Schéma illustrant une page transmettant un port à un service worker pour établir une communication bidirectionnelle.

Récupération en arrière-plan

Navigateurs pris en charge

  • Chrome: 74
  • Edge: 79
  • Firefox: non compatible.
  • Safari: non compatible.

Source

Pour des tâches relativement courtes telles que l'envoi d'un message ou une liste d'URL à mettre en cache, les options abordés jusqu'à présent sont un bon choix. Si la tâche prend trop de temps, le navigateur met fin au service travailleur. Dans le cas contraire, cela présente un risque pour la vie privée de l'utilisateur et pour la batterie.

API Background Fetch Permet de déléguer une tâche longue à un service worker, comme le téléchargement de films, de podcasts ou de niveaux d'un jeu.

Pour communiquer avec le service worker à partir de la page, utilisez backgroundFetch.fetch au lieu de postMessage():

navigator.serviceWorker.ready.then(async (swReg) => {
  const bgFetch = await swReg.backgroundFetch.fetch(
    'my-fetch',
    ['/ep-5.mp3', 'ep-5-artwork.jpg'],
    {
      title: 'Episode 5: Interesting things.',
      icons: [
        {
          sizes: '300x300',
          src: '/ep-5-icon.png',
          type: 'image/png',
        },
      ],
      downloadTotal: 60 * 1024 * 1024,
    },
  );
});

L'objet BackgroundFetchRegistration permet à la page d'écouter l'événement progress à suivre. la progression du téléchargement:

bgFetch.addEventListener('progress', () => {
  // If we didn't provide a total, we can't provide a %.
  if (!bgFetch.downloadTotal) return;

  const percent = Math.round(
    (bgFetch.downloaded / bgFetch.downloadTotal) * 100,
  );
  console.log(`Download progress: ${percent}%`);
});
Schéma illustrant une page transmettant un port à un service worker pour établir une communication bidirectionnelle.
L'interface utilisateur est mise à jour pour indiquer la progression d'un téléchargement (à gauche). Grâce aux service workers, l'opération peut se poursuivre lorsque tous les onglets ont été fermés (à droite).

Étapes suivantes

Dans ce guide, nous avons examiné le cas le plus général de communication entre les Page workers et les service workers. (communication bidirectionnelle).

Souvent, l'un d'eux n'a besoin que d'un contexte pour communiquer avec l'autre, sans recevoir de de réponse. Consultez les guides suivants pour savoir comment implémenter des techniques unidirectionnelles dans vos pages depuis et vers le service worker, ainsi que des cas d'utilisation et des exemples de production:

  • Guide de mise en cache impérative: appeler un service worker depuis la page vers mettre en cache les ressources à l'avance (par exemple, dans les scénarios de préchargement).
  • Diffusion des mises à jour: appel de la page à partir du service worker pour informer sur les mises à jour importantes (par exemple, une nouvelle version de l'application Web est disponible).