Einige Websites müssen möglicherweise mit dem Service Worker kommunizieren, ohne über das Ergebnis informiert zu werden. Hier einige Beispiele:
- Eine Seite sendet dem Service Worker eine Liste von URLs zum Vorabrufen. Wenn der Nutzer auf einen Link klickt, sind das Dokument oder die untergeordneten Seitenressourcen bereits im Cache verfügbar, was die nachfolgende Navigation erheblich beschleunigt.
- Die Seite fordert den Service Worker auf, eine Reihe von Top-Artikeln abzurufen und zu speichern, damit sie offline verfügbar sind.
Wenn Sie diese Arten von nicht kritischen Aufgaben an den Service Worker delegieren, wird der Hauptthread entlastet und kann sich besser um dringendere Aufgaben wie die Reaktion auf Nutzerinteraktionen kümmern.

In diesem Leitfaden wird beschrieben, wie Sie eine Einwegkommunikation von der Seite zum Service Worker mithilfe von Standardbrowser-APIs und der Workbox-Bibliothek implementieren. Wir bezeichnen diese Arten von Anwendungsfällen als imperatives Caching.
Produktionsfall
1-800-Flowers.com hat imperatives Caching (Prefetching) mit Service Workern über postMessage()
implementiert, um die Top-Artikel auf Kategorieseiten vorab abzurufen und so die nachfolgende Navigation zu Produktdetailseiten zu beschleunigen.

Sie verwenden einen gemischten Ansatz, um zu entscheiden, welche Elemente vorab abgerufen werden sollen:
- Beim Laden der Seite wird der Service Worker aufgefordert, die JSON-Daten für die neun wichtigsten Elemente abzurufen und die resultierenden Antwortobjekte dem Cache hinzuzufügen.
- Für die verbleibenden Elemente wird auf das
mouseover
-Ereignis gewartet. Wenn ein Nutzer den Cursor auf ein Element bewegt, kann ein Abruf für die Ressource „on demand“ ausgelöst werden.
Sie verwenden die Cache API, um JSON-Antworten zu speichern:

Wenn der Nutzer auf ein Element klickt, können die zugehörigen JSON-Daten aus dem Cache abgerufen werden, ohne dass eine Netzwerkverbindung erforderlich ist. Dadurch wird die Navigation beschleunigt.
Workbox verwenden
Workbox bietet eine einfache Möglichkeit, Nachrichten über das Paket workbox-window
an einen Service Worker zu senden. Dieses Paket enthält eine Reihe von Modulen, die im Fensterkontext ausgeführt werden sollen. Sie ergänzen die anderen Workbox-Pakete, die im Service Worker ausgeführt werden.
Um mit dem Service Worker zu kommunizieren, müssen Sie zuerst eine Workbox-Objektreferenz zum registrierten Service Worker abrufen:
const wb = new Workbox('/sw.js');
wb.register();
Anschließend können Sie die Nachricht direkt deklarativ senden, ohne sich um die Registrierung, die Aktivierungsprüfung oder die zugrunde liegende Kommunikations-API kümmern zu müssen:
wb.messageSW({"type": "PREFETCH", "payload": {"urls": ["/data1.json", "data2.json"]}}); });
Der Service Worker implementiert einen message
-Handler, um auf diese Nachrichten zu warten. Es kann optional eine Antwort zurückgeben, obwohl dies in solchen Fällen nicht erforderlich ist:
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PREFETCH') {
// do something
}
});
Browser-APIs verwenden
Wenn die Workbox-Bibliothek nicht ausreicht, können Sie die Kommunikation zwischen dem Fenster und dem Service Worker mithilfe von Browser-APIs implementieren.
Mit der postMessage API kann ein unidirektionaler Kommunikationsmechanismus von der Seite zum Service Worker eingerichtet werden.
Auf der Seite wird postMessage()
auf der Service Worker-Schnittstelle aufgerufen:
navigator.serviceWorker.controller.postMessage({
type: 'MSG_ID',
payload: 'some data to perform the task',
});
Der Service Worker implementiert einen message
-Handler, um auf diese Nachrichten zu warten.
self.addEventListener('message', (event) => {
if (event.data && event.data.type === MSG_ID) {
// do something
}
});
Das Attribut {type : 'MSG_ID'}
ist nicht unbedingt erforderlich, aber es ist eine Möglichkeit, dem Service Worker verschiedene Arten von Anweisungen zu senden (z. B. „vorab abrufen“ oder „Speicher löschen“). Der Service Worker kann basierend auf diesem Flag in verschiedene Ausführungspfade verzweigen.
Wenn der Vorgang erfolgreich war, kann der Nutzer davon profitieren. Andernfalls wird der Haupt-User-Flow nicht geändert. Wenn 1-800-Flowers.com beispielsweise versucht, Inhalte vorab im Cache zu speichern, muss die Seite nicht wissen, ob der Service Worker erfolgreich war. Wenn das der Fall ist, profitiert der Nutzer von einer schnelleren Navigation. Wenn nicht, muss die Seite weiterhin zur neuen Seite weitergeleitet werden. Es dauert nur etwas länger.
Einfaches Beispiel für das Prefetching
Eine der häufigsten Anwendungen von imperative caching ist prefetching. Dabei werden Ressourcen für eine bestimmte URL abgerufen, bevor der Nutzer sie aufruft, um die Navigation zu beschleunigen.
Es gibt verschiedene Möglichkeiten, das Prefetching auf Websites zu implementieren:
- Verwendung von Link-Prefetch-Tags auf Seiten: Ressourcen werden fünf Minuten lang im Browsercache gespeichert. Danach gelten die normalen
Cache-Control
-Regeln für die Ressource. - Ergänzen Sie die vorherige Technik mit einer Laufzeit-Caching-Strategie im Service Worker, um die Lebensdauer der Prefetch-Ressource über dieses Limit hinaus zu verlängern.
Für relativ einfache Prefetching-Szenarien, z. B. das Prefetching von Dokumenten oder bestimmten Assets (JS, CSS usw.), sind diese Techniken die beste Lösung.
Wenn zusätzliche Logik erforderlich ist, z. B. das Parsen der Prefetch-Ressource (einer JSON-Datei oder Seite), um die internen URLs abzurufen, ist es besser, diese Aufgabe vollständig an den Service Worker zu delegieren.
Die Delegierung dieser Arten von Vorgängen an den Service Worker hat folgende Vorteile:
- Das Abrufen und die Verarbeitung nach dem Abrufen (die später eingeführt wird) werden auf einen sekundären Thread ausgelagert. Dadurch wird der Hauptthread für wichtigere Aufgaben wie die Reaktion auf Nutzerinteraktionen freigegeben.
- Mehrere Clients (z.B. Tabs) können eine gemeinsame Funktion wiederverwenden und den Dienst sogar gleichzeitig aufrufen, ohne den Hauptthread zu blockieren.
Produktdetailseiten vorab abrufen
Verwenden Sie zuerst postMessage()
in der Service Worker-Schnittstelle und übergeben Sie ein Array von URLs, die im Cache gespeichert werden sollen:
navigator.serviceWorker.controller.postMessage({
type: 'PREFETCH',
payload: {
urls: [
'www.exmaple.com/apis/data_1.json',
'www.exmaple.com/apis/data_2.json',
],
},
});
Implementieren Sie im Service Worker einen message
-Handler, um Nachrichten abzufangen und zu verarbeiten, die von einem aktiven Tab gesendet werden:
addEventListener('message', (event) => {
let data = event.data;
if (data && data.type === 'PREFETCH') {
let urls = data.payload.urls;
for (let i in urls) {
fetchAsync(urls[i]);
}
}
});
Im vorherigen Code haben wir eine kleine Hilfsfunktion namens fetchAsync()
eingeführt, um das Array von URLs zu durchlaufen und für jede URL eine Abrufanfrage zu senden:
async function fetchAsync(url) {
// await response of fetch call
let prefetched = await fetch(url);
// (optionally) cache resources in the service worker storage
}
Wenn die Antwort abgerufen wurde, können Sie sich auf die Caching-Header der Ressource verlassen. In vielen Fällen, z. B. auf Produktinformationsseiten, werden Ressourcen jedoch nicht im Cache gespeichert (d. h., sie haben einen Cache-control
-Header von no-cache
). In solchen Fällen können Sie dieses Verhalten überschreiben, indem Sie die abgerufene Ressource im Service Worker-Cache speichern. Das hat den zusätzlichen Vorteil, dass die Datei auch offline bereitgestellt werden kann.
Über JSON-Daten hinaus
Sobald die JSON-Daten von einem Serverendpunkt abgerufen wurden, enthalten sie oft andere URLs, die ebenfalls vorab abgerufen werden sollten, z. B. ein Bild oder andere Endpunktdaten, die mit diesen Daten der ersten Ebene verknüpft sind.
Angenommen, in unserem Beispiel sind die zurückgegebenen JSON-Daten die Informationen einer Website für Lebensmitteleinkäufe:
{
"productName": "banana",
"productPic": "https://cdn.example.com/product_images/banana.jpeg",
"unitPrice": "1.99"
}
Ändern Sie den fetchAsync()
-Code, um die Liste der Produkte zu durchlaufen und das Hero-Image für jedes Produkt zu cachen:
async function fetchAsync(url, postProcess) {
// await response of fetch call
let prefetched = await fetch(url);
//(optionally) cache resource in the service worker cache
// carry out the post fetch process if supplied
if (postProcess) {
await postProcess(prefetched);
}
}
async function postProcess(prefetched) {
let productJson = await prefetched.json();
if (productJson && productJson.product_pic) {
fetchAsync(productJson.product_pic);
}
}
Sie können für Situationen wie 404-Fehler eine Ausnahmebehandlung für diesen Code hinzufügen. Der Vorteil der Verwendung eines Service Workers zum Prefetching besteht jedoch darin, dass es ohne große Auswirkungen auf die Seite und den Hauptthread fehlschlagen kann. Möglicherweise haben Sie auch eine komplexere Logik für die Nachbearbeitung der vorab abgerufenen Inhalte, wodurch sie flexibler und von den verarbeiteten Daten entkoppelt werden. Alles ist möglich.
Fazit
In diesem Artikel haben wir einen häufigen Anwendungsfall für die Einwegkommunikation zwischen Seite und Service Worker behandelt: imperatives Caching. Die besprochenen Beispiele dienen nur dazu, eine Möglichkeit zur Verwendung dieses Musters zu demonstrieren. Derselbe Ansatz kann auch auf andere Anwendungsfälle angewendet werden, z. B. das Caching von Top-Artikeln auf Anfrage für die Offline-Nutzung oder das Setzen von Lesezeichen.
Weitere Muster für die Kommunikation zwischen Seiten und Service Workern finden Sie hier:
- Broadcast-Updates: Die Seite wird über den Service Worker aufgerufen, um über wichtige Updates zu informieren, z.B. wenn eine neue Version der Web-App verfügbar ist.
- Zwei-Wege-Kommunikation: Eine Aufgabe an einen Service Worker delegieren (z.B. einen umfangreichen Download) und die Seite über den Fortschritt auf dem Laufenden halten.