Signatur
Beschreibung
Das fetch-Ereignis tritt im ServiceWorkerGlobalScope auf, sobald eine Seite, die der Service Worker kontrolliert, eine Netzwerk-Anfrage stellt. Das gilt für Navigationen, für fetch() und XMLHttpRequest sowie für Unterressourcen wie Bilder, Skripte, Stylesheets und Schriftarten. Der Handler bekommt ein FetchEvent-Objekt. Über event.request kann man die Anfrage untersuchen und über event.respondWith() eine eigene Response zurückgeben, zum Beispiel aus dem Cache, frisch aus dem Netz oder selbst erzeugt.
Das Ereignis ist die Grundlage für Offline-Fähigkeit und Caching-Strategien in Progressive Web Apps: Cache First, Network First und Stale-While-Revalidate. Ruft der Handler respondWith() nicht auf, bearbeitet der Browser die Anfrage ganz normal über das Netzwerk.
Man kann den Handler mit self.addEventListener("fetch", handler) registrieren oder über die Eigenschaft self.onfetch = handler setzen. respondWith() muss synchron während der Ereignisbehandlung aufgerufen werden. Übergeben darf man aber ein Promise, das später zu einer Response aufgelöst wird. Hintergrundarbeiten, etwa das Aktualisieren des Caches, verlängert man mit event.waitUntil().
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Name des Ereignisses, hier "fetch". |
|
| $listener Pflicht | (event: FetchEvent) => void | Handler, der ein FetchEvent erhält. Wichtige Bestandteile: event.request (die Request), event.clientId, event.resultingClientId, event.preloadResponse (Promise, bei Navigation Preload), event.handled sowie die Methoden respondWith() und waitUntil(). |
|
| $options | boolean | AddEventListenerOptions | false | Optionale Listener-Einstellungen wie once oder signal. |
Rückgabewert
addEventListener gibt nichts zurück. Die Antwort an die Seite liefert man über event.respondWith(Response | Promise<Response>).Beispiele
Cache First mit Netzwerk-Fallback
// sw.js
const CACHE = "app-v1";
self.addEventListener("fetch", (event) => {
// Nur GET-Anfragen lassen sich cachen
if (event.request.method !== "GET") return;
event.respondWith(
(async () => {
const cached = await caches.match(event.request);
if (cached) return cached;
const response = await fetch(event.request);
// Nur erfolgreiche Antworten vom eigenen Ursprung speichern
if (response.ok && response.type === "basic") {
const cache = await caches.open(CACHE);
// Kopie ablegen, denn der Body lässt sich nur einmal lesen
event.waitUntil(cache.put(event.request, response.clone()));
}
return response;
})()
);
});
Network First für Navigationen mit Offline-Seite
// sw.js
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open("offline").then((cache) => cache.add("/offline.html"))
);
});
self.onfetch = (event) => {
if (event.request.mode !== "navigate") return;
event.respondWith(
(async () => {
try {
// Navigation Preload nutzen, falls aktiviert
const preload = await event.preloadResponse;
if (preload) return preload;
return await fetch(event.request);
} catch {
// Offline: gespeicherte Ersatzseite ausliefern
return caches.match("/offline.html");
}
})()
);
};
Eigene Antwort für eine API-Route erzeugen
// sw.js
self.addEventListener("fetch", (event) => {
const url = new URL(event.request.url);
if (url.origin === self.location.origin && url.pathname === "/api/status") {
const body = JSON.stringify({ online: navigator.onLine, zeit: Date.now() });
event.respondWith(
new Response(body, {
headers: { "Content-Type": "application/json" },
})
);
}
});
// In der Seite:
// const res = await fetch("/api/status");
// console.log(await res.json());
// Wichtig · Fallstricke
respondWith()synchron aufrufen: Kommt der Aufruf erst nach einemawait, wirft der Browser einenInvalidStateError. Die Anfrage geht dann ganz normal ans Netz. Deshalb die asynchrone Logik in ein Promise packen und dieses sofort übergeben.- Nur einmal pro Ereignis:
respondWith()darf pro Ereignis nur einmal aufgerufen werden. Bei mehreren Listenern gewinnt der erste, der es aufruft. - Bodies lassen sich nur einmal lesen: Vor
cache.put()die Antwort mitresponse.clone()kopieren. - Geltungsbereich: Der Service Worker bekommt nur Anfragen von Seiten, die in seinem Scope liegen. Beim allerersten Laden kontrolliert er die Seite noch nicht, es sei denn, man ruft
clients.claim()auf. - Opaque Responses: Anfragen an fremde Ursprünge mit
mode: \"no-cors\"lieferntype === \"opaque\"mit Status 0. Solche Antworten nicht blind cachen, weil sich Fehler darin nicht erkennen lassen und sie viel Speicherplatz belegen. - Nicht-GET-Anfragen: Die Cache-API speichert nur GET-Anfragen. POST-Anfragen und andere Methoden besser durchreichen.
- Sicherheit: Service Worker laufen nur in sicheren Kontexten (HTTPS oder
localhost). Ein fehlerhafter Handler kann die ganze Seite unbenutzbar machen, deshalb Versionen und Aktualisierungen sorgfältig planen. - Kompatibilität: Wird von allen modernen Browsern unterstützt.
preloadResponse(Navigation Preload) funktioniert in Safari erst ab Version 15.4.