Signatur
Beschreibung
resultingClientId ist eine schreibgeschützte Eigenschaft des FetchEvent-Interfaces. Sie ist nur im fetch-Handler eines Service Workers verfügbar. Bei einer Navigationsanfrage, also wenn ein Tab, ein Fenster oder ein <iframe> ein neues Dokument lädt, enthält sie die ID des Clients, der für das neue Dokument angelegt wird. Der Wert steht schon fest, bevor das Dokument überhaupt existiert.
Damit schließt die Eigenschaft eine Lücke: Bei Navigationen ist event.clientId leer, weil die anfragende Seite noch nicht existiert. Über resultingClientId kann der Service Worker die Anfrage trotzdem einem Client zuordnen und ihm später etwas schicken, zum Beispiel über postMessage(). Die Eigenschaft eignet sich auch für Protokollierung, Statistik oder um Zustände pro Seite zu verwalten.
Bei Unterressourcen wie Bildern, Skripten, CSS oder fetch()-Aufrufen einer Seite ist resultingClientId ein leerer String. Dort gibt event.clientId an, welcher Client die Anfrage gestellt hat. Auch bei Anfragen mit dem Ziel "report" bleibt der Wert leer.
Mit clients.get(event.resultingClientId) bekommst du das zugehörige Client-Objekt. Dieser Client entsteht aber erst, wenn die Antwort ausgeliefert und das Dokument geladen wird. Deshalb gehört das Warten auf den Client in event.waitUntil() und nicht in event.respondWith().
Rückgabewert
"".Beispiele
Navigationen und Unterressourcen unterscheiden
// sw.js
self.addEventListener('fetch', (event) => {
if (event.request.mode === 'navigate') {
console.log('Navigation, neuer Client:', event.resultingClientId);
console.log('clientId (leer):', JSON.stringify(event.clientId));
} else {
console.log('Unterressource von Client:', event.clientId);
console.log('resultingClientId (leer):', JSON.stringify(event.resultingClientId));
}
});
Neu geladener Seite nach der Navigation eine Nachricht schicken
// sw.js
self.addEventListener('fetch', (event) => {
if (event.request.mode !== 'navigate') return;
// Antwort sofort liefern, nicht auf den Client warten
event.respondWith(fetch(event.request));
// Auf den neuen Client in waitUntil warten
event.waitUntil((async () => {
const id = event.resultingClientId;
if (!id) return;
const client = await self.clients.get(id);
client?.postMessage({ type: 'sw-hallo', url: event.request.url });
})());
});
// main.js (Seite)
navigator.serviceWorker.addEventListener('message', (e) => {
if (e.data.type === 'sw-hallo') {
console.log('Service Worker grüßt für', e.data.url);
}
});
Seitenaufrufe pro Client protokollieren
// sw.js
const seitenaufrufe = new Map();
self.addEventListener('fetch', (event) => {
const { request, resultingClientId } = event;
if (request.mode === 'navigate' && resultingClientId) {
seitenaufrufe.set(resultingClientId, {
url: request.url,
zeit: Date.now()
});
console.log(`Clients mit Navigation: ${seitenaufrufe.size}`);
}
});
// Wichtig · Fallstricke
- Keine Blockade in
respondWith(): Warte nie innerhalb der Promise vonrespondWith()aufclients.get(event.resultingClientId). Der Client entsteht erst, wenn die Antwort da ist. Warten auf ihn kann deshalb hängen bleiben oderundefinedliefern. Verwende stattdessenevent.waitUntil(). - Leerer String statt
null: Ohne Navigation ist der Wert"". Prüfe ihn mit einer Wahrheitsprüfung (if (id)) und nicht mit!== null. - Nicht verwechseln:
clientIdist der anfragende Client (bei Navigationen leer).resultingClientIdist der Client, der durch die Navigation entsteht. Das älterereplacesClientIdsteht in der Spezifikation, kaum ein Browser unterstützt es. - Kompatibilität: Chromium-Browser unterstützen die Eigenschaft seit Version 72, Firefox seit Version 65, Safari erst in neueren Versionen. Sie gibt es nur im Service-Worker-Kontext und nur über HTTPS oder
localhost. In älteren Browsern ist sieundefined. - Die ID ist ein zufälliger, undurchsichtiger String. Werte ihn nicht aus und speichere ihn nicht dauerhaft, denn er gilt nur, solange der Client existiert.