Aktualisierungen an Seiten mit Service Workern senden

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

In einigen Szenarien muss der Service Worker möglicherweise proaktiv mit einem der aktiven Tabs kommunizieren, die er steuert, um über ein bestimmtes Ereignis zu informieren. Beispiele:

  • Die Seite wird informiert, wenn eine neue Version des Service Workers installiert wurde, damit dem Nutzer die Schaltfläche Update to update (Zum Aktualisieren aktualisieren) angezeigt wird, damit der Nutzer sofort auf die neue Funktion zugreifen kann.
  • Der Nutzer wird über eine Änderung an den im Cache gespeicherten Daten informiert, die auf der Dienst-Worker-Seite vorgenommen wurde. Dazu wird ein Hinweis eingeblendet, z. B. The app is now ready to work offline oder "New version of the content available" (Neue Version des Inhalts verfügbar).
Diagramm, das einen Service Worker zeigt, der mit der Seite kommuniziert, um ein Update zu senden

Diese Anwendungsfälle, bei denen der Service Worker keine Nachricht von der Seite erhalten muss, um eine Kommunikation zu starten, werden als „Broadcast-Aktualisierungen“ bezeichnet. In diesem Leitfaden werden verschiedene Möglichkeiten zur Implementierung dieser Art der Kommunikation zwischen Seiten und Service Workern mithilfe von Standard-Browser-APIs und der Workbox-Bibliothek beschrieben.

Produktionsfälle

Tinder

Die Tinder-PWA verwendet workbox-window, um wichtige Lebenszyklusmomente von Service Workern auf der Seite zu überwachen („installiert“, „kontrolliert“ und „aktiviert“). Wenn also ein neuer Dienst-Worker ins Spiel kommt, wird ein Banner mit der Meldung Update verfügbar angezeigt, damit Nutzer die PWA aktualisieren und auf die neuesten Funktionen zugreifen können:

Screenshot der Webapp-Funktion von Tinder „Update verfügbar“
In der Tinder-PWA teilt der Service Worker der Seite mit, dass eine neue Version verfügbar ist. Daraufhin wird den Nutzern auf der Seite ein Banner mit der Meldung „Update verfügbar“ angezeigt.

Squoosh

In der Squoosh-PWA sendet der Dienst-Worker, wenn er alle erforderlichen Assets für die Offlinenutzung im Cache gespeichert hat, eine Nachricht an die Seite, um ein Toast-Fenster mit der Meldung „Bereit für die Offlinenutzung“ anzuzeigen, das den Nutzer über die Funktion informiert:

Screenshot der Funktion „Bereit für den Offlinezugriff“ von Squoosh Webapp.
In der Squoosh-PWA sendet der Service Worker eine Aktualisierung an die Seite, wenn der Cache bereit ist. Auf der Seite wird dann die Meldung „Offline verfügbar“ angezeigt.

Workbox verwenden

Auf Service Worker-Lifecycle-Ereignisse warten

workbox-window bietet eine einfache Schnittstelle, um wichtige Lebenszyklusereignisse von Dienstmitarbeitern zu überwachen. Im Hintergrund verwendet die Bibliothek clientseitige APIs wie updatefound und statechange und bietet Ereignislistener auf höherer Ebene im workbox-window-Objekt, was die Nutzung dieser Ereignisse für den Nutzer erleichtert.

Mit dem folgenden Seitencode können Sie jedes Mal erkennen, wenn eine neue Version des Dienst-Workers installiert wird, und den Nutzer darüber informieren:

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

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

wb.register();

Seite über Änderungen an Cache-Daten informieren

Das Workbox-Paket workbox-broadcast-update bietet eine Standardmethode, um Window-Clients darüber zu informieren, dass eine im Cache gespeicherte Antwort aktualisiert wurde. Dieser wird am häufigsten zusammen mit der Strategie StaleWhileRevalidate verwendet.

Wenn Sie Aktualisierungen übertragen möchten, fügen Sie Ihren Strategieoptionen auf der Service Worker-Seite ein broadcastUpdate.BroadcastUpdatePlugin hinzu:

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(),
    ],
  })
);

In Ihrer Webanwendung können Sie beispielsweise auf folgende Ereignisse warten:

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();
  }
});

Browser-APIs verwenden

Wenn die von Workbox bereitgestellte Funktionalität für Ihre Anforderungen nicht ausreicht, verwenden Sie die folgenden Browser-APIs, um "broadcast updates" (Broadcast-Updates) zu implementieren:

Broadcast Channel API

Der Service Worker erstellt ein BroadcastChannel-Objekt und sendet Nachrichten an dieses. Jeder Kontext (z.B. eine Seite), der am Empfang dieser Nachrichten interessiert ist, kann ein BroadcastChannel-Objekt instanziieren und einen Nachrichten-Handler implementieren, um Nachrichten zu empfangen.

Verwenden Sie den folgenden Code, um die Seite darüber zu informieren, dass ein neuer Dienst-Worker installiert wurde:

// 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'});
});

Die Seite erfasst diese Ereignisse, indem sie sw-update-channel abonniert:

// 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.
  }
};

Dies ist eine einfache Methode, aber ihre Einschränkung ist die Browserunterstützung: Zum Zeitpunkt der Veröffentlichung dieses Artikels unterstützt Safari diese API nicht.

Client API

Die Client API bietet eine einfache Möglichkeit, über den Service Worker mit mehreren Clients zu kommunizieren, indem ein Array von Client-Objekten durchlaufen wird.

Verwenden Sie den folgenden Service Worker-Code, um eine Nachricht an den letzten hervorgehobenen Tab zu senden:

// 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'});
  }
});

Auf der Seite wird ein Nachrichten-Handler implementiert, um diese Nachrichten abzufangen:

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

Die Client API ist eine gute Option für Fälle wie das Übertragen von Informationen an mehrere aktive Tabs. Die API wird von allen gängigen Browsern unterstützt, nicht alle ihre Methoden. Prüfen Sie vor der Verwendung, ob Ihr Browser unterstützt wird.

Kanal für Nachrichten

Für den Nachrichtenkanal ist ein erster Konfigurationsschritt erforderlich. Dabei wird ein Port von der Seite an den Service Worker übergeben, um einen Kommunikationskanal zwischen ihnen einzurichten. Die Seite instanziiert ein MessageChannel-Objekt und übergibt über die postMessage()-Schnittstelle einen Port an den Service Worker:

const messageChannel = new MessageChannel();

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

Die Seite wartet auf Nachrichten, indem sie einen "onmessage"-Handler an diesem Port implementiert:

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

Der Service Worker empfängt den Port und speichert eine Referenz darauf:

// Initialize
let communicationPort;

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

Von diesem Punkt aus kann es Nachrichten an die Seite senden, indem es postMessage() in der Referenz zum Port aufruft:

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

MessageChannel ist aufgrund der Notwendigkeit, Ports zu initialisieren, möglicherweise komplexer zu implementieren, wird aber von allen gängigen Browsern unterstützt.

Nächste Schritte

In diesem Leitfaden haben wir einen bestimmten Fall der Kommunikation zwischen Fenster und Dienst-Worker untersucht: „Broadcast-Aktualisierungen“. Zu den Beispielen gehören das Abhören wichtiger Lebenszyklusereignisse von Service Workern und die Kommunikation von Änderungen an Inhalten oder im Cache an die Seite. Es gibt interessantere Anwendungsfälle, bei denen der Service Worker proaktiv mit der Seite kommuniziert, ohne zuvor eine Nachricht zu erhalten.

Weitere Muster für die Kommunikation zwischen Fenster und Dienstworkern finden Sie hier:

  • Imperatives Caching: Ein Service Worker wird von der Seite aufgerufen, um Ressourcen im Voraus im Cache zu speichern (z. B. in Prefetching-Szenarien).
  • Zwei-Wege-Kommunikation: Eine Aufgabe wird an einen Service Worker delegiert (z.B. ein umfangreicher Download) und die Seite wird über den Fortschritt informiert.

Zusätzliche Ressourcen