Signatur
Beschreibung
Das message-Ereignis des ServiceWorkerGlobalScope tritt auf, wenn der Service Worker eine Nachricht empfängt. Meist kommt sie von einer kontrollierten Seite, die navigator.serviceWorker.controller.postMessage() oder registration.active.postMessage() aufruft. Der Handler bekommt ein ExtendableMessageEvent. Dessen Eigenschaft data enthält die Nutzdaten, source verweist auf den sendenden Client (bzw. ServiceWorker oder MessagePort).
Registrieren kannst du den Handler auf zwei Wegen: mit self.addEventListener("message", handler) oder über die Eigenschaft self.onmessage = handler. Typische Anwendungsfälle sind:
skipWaiting()auf Knopfdruck auslösen (Muster „Update verfügbar“)- den Cache gezielt befüllen oder leeren
- den Status des Workers abfragen
- Daten an andere Tabs weiterverteilen
Weil ExtendableMessageEvent von ExtendableEvent erbt, kannst du mit event.waitUntil(promise) dafür sorgen, dass der Browser den Worker erst beendet, wenn die asynchrone Arbeit abgeschlossen ist. Antworten schickst du entweder direkt über event.source.postMessage() oder über einen mitgeschickten MessagePort in event.ports.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Ereignistyp, hier "message". |
|
| $listener Pflicht | (event: ExtendableMessageEvent) => void | Handler-Funktion. Wichtige Eigenschaften des Ereignisobjekts: data (Nutzdaten), source (Client | ServiceWorker | MessagePort | null), origin, ports (übertragene MessagePort-Objekte), lastEventId. |
|
| $options | boolean | AddEventListenerOptions | false | Optionale Listener-Optionen wie once oder signal. |
Rückgabewert
addEventListener gibt nichts zurück. Den Rückgabewert des Handlers ignoriert der Browser. Für asynchrone Arbeit verwendest du event.waitUntil().Beispiele
SKIP_WAITING-Muster: Update auf Nutzerwunsch aktivieren
// sw.js
self.addEventListener("message", (event) => {
if (event.data?.type === "SKIP_WAITING") {
self.skipWaiting();
}
});
// main.js (Seite)
const reg = await navigator.serviceWorker.getRegistration();
if (reg?.waiting) {
reg.waiting.postMessage({ type: "SKIP_WAITING" });
}
Antwort an den sendenden Tab mit waitUntil
// sw.js
self.addEventListener("message", (event) => {
if (event.data?.type !== "CACHE_URLS") return;
event.waitUntil((async () => {
const cache = await caches.open("runtime-v1");
await cache.addAll(event.data.urls);
// event.source ist der Client, der die Nachricht geschickt hat
event.source?.postMessage({ type: "CACHED", count: event.data.urls.length });
})());
});
// main.js
navigator.serviceWorker.addEventListener("message", (e) => console.log(e.data));
navigator.serviceWorker.controller?.postMessage({
type: "CACHE_URLS",
urls: ["/", "/styles.css", "/app.js"]
});
Anfrage/Antwort über MessageChannel (onmessage-Property)
// sw.js
const VERSION = "1.4.2";
self.onmessage = (event) => {
if (event.data === "GET_VERSION") {
event.ports[0]?.postMessage(VERSION);
}
};
// main.js
const askVersion = () => new Promise((resolve) => {
const { port1, port2 } = new MessageChannel();
port1.onmessage = (e) => resolve(e.data);
navigator.serviceWorker.controller.postMessage("GET_VERSION", [port2]);
});
console.log("SW-Version:", await askVersion());
Nachricht an alle offenen Tabs verteilen
// sw.js
self.addEventListener("message", (event) => {
if (event.data?.type !== "BROADCAST") return;
event.waitUntil((async () => {
const clients = await self.clients.matchAll({ type: "window", includeUncontrolled: true });
for (const client of clients) {
if (client.id !== event.source?.id) {
client.postMessage({ type: "SYNC", payload: event.data.payload });
}
}
})());
});
// Wichtig · Fallstricke
- Absender prüfen: Nachrichten können nur von Clients desselben Ursprungs kommen. Validiere trotzdem
event.data(Typ, Struktur), bevor du davon abhängige Aktionen wie Cache-Löschung ausführst. Werte indatagelten als nicht vertrauenswürdige Eingabe. - Kein Controller: Bei einem harten Neuladen (Umschalt+F5) oder beim ersten Besuch ist
navigator.serviceWorker.controllergleichnull. Nutze dannregistration.activeoder warte aufnavigator.serviceWorker.ready. - Lebensdauer: Nach dem Ereignis kann der Browser den Worker jederzeit beenden. Asynchrone Arbeit gehört deshalb in
event.waitUntil(). Globale Variablen bleiben zwischen den Ereignissen nicht zuverlässig erhalten. - Serialisierung:
datawird per Structured Clone übertragen. Funktionen, DOM-Knoten und Klasseninstanzen (deren Prototyp verloren geht) lassen sich nicht senden. Große Puffer überträgst du effizient als Transferable. - Kann eine Nachricht nicht deserialisiert werden, feuert stattdessen
messageerror. - Die Seite empfängt Antworten über
navigator.serviceWorker(message-Ereignis amServiceWorkerContainer), nicht überwindow. Service Worker laufen nur in sicheren Kontexten (HTTPS oder localhost).