Progressive Web-Apps sind zuverlässig. Sie können Assets schnell laden, Nutzer bei Laune halten und auch bei schlechten Netzwerkverbindungen sofort Feedback geben. Wie ist das möglich? Das liegt am Service Worker-Ereignis fetch
.
Das Abrufereignis
Mit dem Ereignis fetch
können wir jede Netzwerkanfrage abfangen, die von der PWA im Bereich des Dienstarbeiters gesendet wird, sowohl für Anfragen mit gleicher als auch mit unterschiedlicher Herkunft. Neben Navigations- und Asset-Anfragen ermöglicht das Abrufen von einem installierten Service Worker auch das Rendern von Seitenaufrufen nach dem ersten Laden einer Website ohne Netzwerkanrufe.
Der fetch
-Handler empfängt alle Anfragen von einer App, einschließlich URLs und HTTP-Headern, und lässt den App-Entwickler entscheiden, wie sie verarbeitet werden sollen.
Ihr Service Worker kann eine Anfrage an das Netzwerk weiterleiten, mit einer zuvor im Cache gespeicherten Antwort antworten oder eine neue Antwort erstellen. Die Entscheidung liegt ganz bei dir. Hier ein einfaches Beispiel:
self.addEventListener("fetch", event => {
console.log(`URL requested: ${event.request.url}`);
});
Auf eine Anfrage antworten
Wenn eine Anfrage an Ihren Service Worker gesendet wird, haben Sie zwei Möglichkeiten: Sie können sie ignorieren, wodurch sie an das Netzwerk weitergeleitet wird, oder Sie können darauf reagieren. Wenn Sie Anfragen über Ihren Service Worker beantworten, können Sie festlegen, was und wie es an Ihre PWA zurückgegeben wird, auch wenn der Nutzer offline ist.
Wenn Sie auf eine eingehende Anfrage reagieren möchten, rufen Sie event.respondWith()
in einem fetch
-Ereignis-Handler auf. So gehts:
// fetch event handler in your service worker file
self.addEventListener("fetch", event => {
const response = .... // a response or a Promise of response
event.respondWith(response);
});
Du musst respondWith()
synchron aufrufen und ein Response-Objekt zurückgeben. Sie können respondWith()
jedoch nicht nach Abschluss des Fetch-Ereignis-Handlers aufrufen, z. B. in einem asynchronen Aufruf. Wenn du auf die vollständige Antwort warten musst, kannst du respondWith()
ein Versprechen übergeben, das mit einer Antwort aufgelöst wird.
Antworten erstellen
Mit der Fetch API können Sie HTTP-Antworten in Ihrem JavaScript-Code erstellen. Diese Antworten können mit der Cache Storage API im Cache gespeichert und so zurückgegeben werden, als würden sie von einem Webserver stammen.
Erstelle zum Erstellen einer Antwort ein neues Response
-Objekt und lege den Textkörper sowie Optionen wie Status und Header fest:
const simpleResponse = new Response("Body of the HTTP response");
const options = {
status: 200,
headers: {
'Content-type': 'text/html'
}
};
const htmlResponse = new Response("<b>HTML</b> content", options)
Antworten aus dem Cache
Nachdem Sie nun wissen, wie Sie HTTP-Antworten von einem Service Worker ausliefern, ist es an der Zeit, die Caching Storage-Benutzeroberfläche zu verwenden, um Assets auf dem Gerät zu speichern.
Mit der Cache Storage API können Sie prüfen, ob die von der PWA empfangene Anfrage im Cache verfügbar ist. Ist das der Fall, antworten Sie mit ihr auf respondWith()
.
Dazu müssen Sie zuerst im Cache suchen. Die Funktion match()
, die in der caches
-Oberfläche auf oberster Ebene verfügbar ist, sucht in allen Geschäften in Ihrem Ursprung oder in einem einzelnen offenen Cacheobjekt.
Die Funktion match()
empfängt eine HTTP-Anfrage oder eine URL als Argument und gibt ein Versprechen zurück, das mit der dem entsprechenden Schlüssel zugeordneten Antwort aufgelöst wird.
// Global search on all caches in the current origin
caches.match(urlOrRequest).then(response => {
console.log(response ? response : "It's not in the cache");
});
// Cache-specific search
caches.open("pwa-assets").then(cache => {
cache.match(urlOrRequest).then(response => {
console.log(response ? response : "It's not in the cache");
});
});
Caching-Strategien
Das Bereitstellen von Dateien nur aus dem Browser-Cache ist nicht für jeden Anwendungsfall geeignet. Beispielsweise kann der Nutzer oder der Browser den Cache leeren. Daher sollten Sie Ihre eigenen Strategien für die Bereitstellung von Assets für Ihre PWA definieren.
Sie sind nicht auf eine Caching-Strategie beschränkt. Sie können für verschiedene URL-Muster unterschiedliche definieren. Sie können beispielsweise eine Strategie für die minimalen UI-Assets, eine andere für API-Aufrufe und eine dritte für Bild- und Daten-URLs haben.
Lesen Sie dazu event.request.url
in ServiceWorkerGlobalScope.onfetch
und parsen Sie sie mit regulären Ausdrücken oder einem URL-Muster. (Zum Zeitpunkt der Erstellung dieses Artikels wird das URL-Muster nicht auf allen Plattformen unterstützt.)
Die gängigsten Strategien sind:
- Cache zuerst
- Sucht zuerst nach einer im Cache gespeicherten Antwort und greift auf das Netzwerk zurück, falls keine gefunden wird.
- Netzwerk zuerst
- Erfordert zuerst eine Antwort vom Netzwerk und sucht dann, falls keine Antwort zurückgegeben wird, im Cache nach einer Antwort.
- Veraltet bei Neuvalidierung
- Eine Antwort wird aus dem Cache bereitgestellt, während im Hintergrund die neueste Version angefordert und für die nächste Anfrage des Assets im Cache gespeichert wird.
- Nur Netzwerk
- Antwortet immer mit einer Antwort vom Netzwerk oder es kommt zu einem Fehler. Der Cache wird nie konsultiert.
- Nur Cache
- Antwortet immer mit einer Antwort aus dem Cache oder es kommt zu einem Fehler. Das Netzwerk wird nie konsultiert. Die Assets, die mit dieser Strategie ausgeliefert werden, müssen dem Cache hinzugefügt werden, bevor sie angefordert werden.
Zuerst im Cache speichern
Bei dieser Strategie sucht der Service Worker im Cache nach der übereinstimmenden Anfrage und gibt die entsprechende Antwort zurück, falls sie im Cache gespeichert ist. Andernfalls wird die Antwort aus dem Netzwerk abgerufen und der Cache für zukünftige Aufrufe optional aktualisiert. Wenn weder eine Cache-Antwort noch eine Netzwerkantwort vorhanden ist, schlägt die Anfrage fehl. Da das Bereitstellen von Assets ohne Zugriff auf das Netzwerk in der Regel schneller ist, wird bei dieser Strategie die Leistung vor Aktualität priorisiert.
self.addEventListener("fetch", event => {
event.respondWith(
caches.match(event.request)
.then(cachedResponse => {
// It can update the cache to serve updated content on the next request
return cachedResponse || fetch(event.request);
}
)
)
});
Netzwerk zuerst
Diese Strategie ist das Spiegelbild der Cache-first-Strategie. Dabei wird geprüft, ob die Anfrage über das Netzwerk erfüllt werden kann. Falls nicht, wird versucht, sie aus dem Cache abzurufen. Cache zuerst. Wenn weder eine Netzwerk- noch eine Cache-Antwort vorhanden ist, schlägt die Anfrage fehl. Da die Antwort vom Netzwerk in der Regel langsamer als vom Cache abgerufen wird, werden bei dieser Strategie aktualisierte Inhalte anstelle der Leistung priorisiert.
self.addEventListener("fetch", event => {
event.respondWith(
fetch(event.request)
.catch(error => {
return caches.match(event.request) ;
})
);
});
Veraltet bei nochmaliger Validierung
Bei der Strategie „Stale while revalidate“ (Alt, aber noch einmal bestätigen) wird sofort eine im Cache gespeicherte Antwort zurückgegeben. Anschließend wird das Netzwerk auf eine Aktualisierung geprüft und die im Cache gespeicherte Antwort wird ersetzt, falls eine gefunden wird. Bei dieser Strategie wird immer eine Netzwerkanfrage gestellt, da auch dann, wenn eine im Cache gespeicherte Ressource gefunden wird, versucht wird, den Cache mit den Daten aus dem Netzwerk zu aktualisieren, um die aktualisierte Version bei der nächsten Anfrage zu verwenden. Mit dieser Strategie können Sie also von der schnellen Bereitstellung der Cache-first-Strategie profitieren und den Cache im Hintergrund aktualisieren.
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cachedResponse => {
const networkFetch = fetch(event.request).then(response => {
// update the cache with a clone of the network response
const responseClone = response.clone()
caches.open(url.searchParams.get('name')).then(cache => {
cache.put(event.request, responseClone)
})
return response
}).catch(function (reason) {
console.error('ServiceWorker fetch failed: ', reason)
})
// prioritize cached response over network
return cachedResponse || networkFetch
}
)
)
})
Nur Netzwerk
Die reine Netzwerkstrategie ähnelt dem Verhalten von Browsern ohne Service Worker oder Cache Storage API. Bei Anfragen wird nur eine Ressource zurückgegeben, wenn sie aus dem Netzwerk abgerufen werden kann. Das ist oft nützlich für Ressourcen wie nur online verfügbare API-Anfragen.
Nur Cache
Bei der Cache-only-Strategie werden Anfragen nie an das Netzwerk gesendet. Auf alle eingehenden Anfragen wird mit einem vorab ausgefüllten Cache-Element geantwortet. Im folgenden Code wird der fetch
-Ereignishandler mit der match
-Methode des Cache-Speichers verwendet, um nur auf den Cache zu antworten:
self.addEventListener("fetch", event => {
event.respondWith(caches.match(event.request));
});
Benutzerdefinierte Strategien
Die oben genannten sind gängige Caching-Strategien. Sie sind jedoch für Ihren Service Worker und die Verarbeitung von Anfragen verantwortlich. Wenn keine dieser Vorlagen Ihren Anforderungen entspricht, können Sie Ihre eigene erstellen.
Sie können beispielsweise eine Netzwerk-first-Strategie mit einem Zeitlimit verwenden, um aktualisierte Inhalte zu priorisieren, aber nur, wenn die Antwort innerhalb eines von Ihnen festgelegten Grenzwerts angezeigt wird. Sie können auch eine im Cache gespeicherte Antwort mit einer Netzwerkantwort zusammenführen und eine komplexe Antwort vom Service Worker erstellen.
Assets aktualisieren
Es kann schwierig sein, die im Cache Ihrer PWA gespeicherten Assets auf dem neuesten Stand zu halten. Die Strategie „Stale while revalidate“ ist eine Möglichkeit, dies zu tun, aber nicht die einzige. Im Kapitel „Aktualisieren“ erfahren Sie, wie Sie die Inhalte und Assets Ihrer App auf dem neuesten Stand halten.