Service-Worker sind leistungsstark und absolut lernenswert. Mit ihnen können Sie Ihren Nutzern ein völlig neues Erlebnis bieten. Ihre Website kann sofort geladen werden. Sie kann offline funktionieren. Sie kann als native Anwendung installiert werden und sich genauso gut anfühlen – aber mit der Reichweite und Freiheit des Webs.
Aber Service Worker sind anders als alles, was die meisten von uns Webentwicklern gewohnt sind. Sie haben eine steile Lernkurve und eine Handvoll Fallstricke, auf die man aufpassen muss.
Google Developers und ich haben kürzlich gemeinsam an einem Projekt gearbeitet – Service Workies – ein kostenloses Spiel zum Verständnis von Service Workern. Bei der Entwicklung des Spiels und der Arbeit mit den komplexen Aspekten von Service Workies bin ich auf ein paar Probleme gestoßen. Was mir am meisten geholfen hat, war, eine Handvoll anschaulicher Metaphern zu finden. In diesem Beitrag werden wir diese mentalen Modelle erforschen und uns die paradoxen Eigenschaften zu Gemüte führen, die Service Worker sowohl knifflig als auch großartig machen.
- Das Gleiche, aber anders #
- Eine neue Schicht #
- Mächtig, aber begrenzt #
- Langlebig, aber kurzlebig #
- Stopped #
- waitUntil #
- Achtet auf den globalen Status #
- Zusammen, aber getrennt #
- Mit den Caches eines anderen Service Workers spielen #
- Skip skipWaiting #
- Sauber starten #
- End clean #
- Mentalität der Service Worker #
Das Gleiche, aber anders #
Während Sie Ihren Service Worker programmieren, werden Ihnen viele Dinge vertraut vorkommen. Sie können Ihre bevorzugten neuen JavaScript-Sprachfunktionen verwenden. Sie hören auf Lifecycle-Ereignisse, genau wie bei UI-Ereignissen. Sie verwalten den Kontrollfluss mit Versprechen, wie Sie es gewohnt sind.
Aber andere Verhaltensweisen von Service Workern lassen Sie verwirrt den Kopf kratzen. Vor allem, wenn Sie die Seite aktualisieren und nicht sehen, dass Ihre Code-Änderungen angewendet werden.
Eine neue Schicht #
Normalerweise müssen Sie beim Aufbau einer Website nur an zwei Schichten denken: den Client und den Server. Der Service Worker ist eine ganz neue Schicht, die in der Mitte sitzt.
Stellen Sie sich Ihren Service Worker als eine Art Browser-Erweiterung vor – eine, die Ihre Site im Browser des Benutzers installieren kann. Nach der Installation erweitert der Service Worker den Browser für Ihre Website um eine leistungsstarke Zwischenschicht. Diese Service-Worker-Schicht kann alle Anfragen Ihrer Website abfangen und bearbeiten.
Die Service-Worker-Schicht hat ihren eigenen Lebenszyklus, der unabhängig von der Browser-Registerkarte ist. Eine einfache Seitenaktualisierung reicht nicht aus, um einen Service Worker zu aktualisieren – genauso wenig wie Sie erwarten würden, dass eine Seitenaktualisierung den auf einem Server bereitgestellten Code aktualisiert. Jede Schicht hat ihre eigenen Regeln für die Aktualisierung.
Im Service Workies Spiel decken wir die vielen Details des Service Worker Lebenszyklus ab und geben Ihnen eine Menge Übung, um damit zu arbeiten.
Betrachten Sie Ihren Service Worker als eine neue mittlere Schicht mit eigenem Lebenszyklus und Methoden für die Aktualisierung.
Mächtig, aber begrenzt #
Die Verwendung eines Service Workers auf Ihrer Website bietet Ihnen unglaubliche Vorteile. Ihre Website kann:
- auch dann einwandfrei funktionieren, wenn der Benutzer offline ist
- Massive Leistungsverbesserungen durch Caching erzielen
- Push-Benachrichtigungen verwenden
- als PWA installiert werden
So viel Service Worker auch können, sie sind vom Design her begrenzt. Sie können nichts synchron oder im selben Thread wie Ihre Website tun. Das bedeutet keinen Zugriff auf:
- localStorage
- das DOM
- das Fenster
Die gute Nachricht ist, dass es eine Handvoll Möglichkeiten gibt, wie Ihre Seite mit ihrem Service Worker kommunizieren kann, einschließlich direkter postMessage
, eins-zu-eins Nachrichtenkanäle und eins-zu-viele Broadcast-Kanäle.
Stellen Sie sich Ihren Service Worker als etwas vor, das außerhalb Ihrer Seite lebt. Sie können mit ihm reden, aber er kann nicht direkt auf Ihre Seite zugreifen.
Langlebig, aber kurzlebig #
Ein aktiver Service Worker lebt weiter, auch wenn ein Benutzer Ihre Seite verlässt oder die Registerkarte schließt. Der Browser behält diesen Service Worker bei, damit er bereit ist, wenn der Benutzer das nächste Mal auf Ihre Website zurückkehrt. Bevor die allererste Anfrage gestellt wird, hat der Service Worker die Möglichkeit, sie abzufangen und die Kontrolle über die Seite zu übernehmen. Dies ermöglicht es einer Website, offline zu arbeiten – der Service Worker kann eine zwischengespeicherte Version der Seite selbst bereitstellen, selbst wenn der Benutzer keine Verbindung zum Internet hat.
In Service Workies visualisieren wir dieses Konzept mit Kolohe (einem freundlichen Service Worker), der Anfragen abfängt und bearbeitet.
Stopped #
Trotz der Tatsache, dass Service Worker unsterblich zu sein scheinen, können sie fast jederzeit angehalten werden. Der Browser will keine Ressourcen für einen Service Worker verschwenden, der gerade nichts tut. Angehalten zu werden ist nicht dasselbe wie beendet zu werden – der Service Worker bleibt installiert und aktiviert. Er wird lediglich in den Ruhezustand versetzt. Wenn er das nächste Mal gebraucht wird (z.B. um eine Anfrage zu bearbeiten), weckt der Browser ihn wieder auf.
waitUntil #
Aufgrund der ständigen Möglichkeit, in den Schlaf versetzt zu werden, braucht Ihr Service-Worker eine Möglichkeit, dem Browser mitzuteilen, dass er gerade etwas Wichtiges tut und keine Lust auf ein Nickerchen hat. An dieser Stelle kommt event.waitUntil()
ins Spiel. Diese Methode erweitert den Lebenszyklus, in dem sie verwendet wird, und verhindert, dass sie angehalten wird und in die nächste Phase ihres Lebenszyklus übergeht, bis wir bereit sind. Das gibt uns Zeit, Caches einzurichten, Ressourcen aus dem Netzwerk zu holen, etc.
Dieses Beispiel sagt dem Browser, dass unser Service Worker nicht mit der Installation fertig ist, bis der assets
Cache erstellt und mit dem Bild eines Schwertes gefüllt wurde:
self.addEventListener("install", event => {
event.waitUntil(
caches.open("assets").then(cache => {
return cache.addAll();
})
);
});
Achtet auf den globalen Status #
Wenn dieser Start/Stop passiert, wird der globale Bereich des Service Workers zurückgesetzt. Achten Sie also darauf, dass Sie keinen globalen Zustand in Ihrem Service Worker verwenden, sonst werden Sie traurig sein, wenn er das nächste Mal aufwacht und einen anderen Zustand hat als den, den er erwartet hat.
Betrachten Sie dieses Beispiel, das einen globalen Zustand verwendet:
const favoriteNumber = Math.random();
let hasHandledARequest = false;
self.addEventListener("fetch", event => {
console.log(favoriteNumber);
console.log(hasHandledARequest);
hasHandledARequest = true;
});
Bei jeder Anfrage protokolliert dieser Service Worker eine Zahl – sagen wir 0.13981866382421893
. Die Variable hasHandledARequest
ändert sich ebenfalls in true
. Jetzt ist der Service Worker eine Zeit lang untätig, so dass der Browser ihn anhält. Bei der nächsten Anfrage wird der Service Worker wieder benötigt, also weckt der Browser ihn auf. Sein Skript wird erneut ausgewertet. Jetzt wird hasHandledARequest
auf false
zurückgesetzt, und favoriteNumber
ist etwas völlig anderes – 0.5907281835659033
.
Sie können sich nicht auf einen gespeicherten Zustand in einem Service Worker verlassen. Außerdem kann das Erstellen von Instanzen von Dingen wie Message Channels zu Fehlern führen: Sie erhalten jedes Mal, wenn der Service Worker stoppt/startet, eine brandneue Instanz.
Warnung: Dieser Fehler ist besonders wichtig, wenn Sie an Ihrem Service-Worker-Code arbeiten, denn wenn Chrome DevTools geöffnet ist, ist das Start/Stop-Verhalten deaktiviert. Es kann sein, dass Sie Fehler, die durch den globalen Zustand verursacht werden, erst dann sehen, wenn sie an Ihre Benutzer ausgeliefert wurden.
In Service Workies Kapitel 3 stellen wir uns unseren gestoppten Service Worker so vor, dass er alle Farbe verliert, während er darauf wartet, geweckt zu werden.
Stellen Sie sich Ihren Service Worker wie einen Whippet-Hund vor. Er ist schnell, loyal und großartig. Er bleibt immer an deiner Seite, egal was passiert. Aber meistens will er nur schlafen. Die ganze Zeit. Du musst ihn wissen lassen, wann er wach bleiben soll. Guter Hund!
Zusammen, aber getrennt #
Deine Seite kann immer nur von einem Service Worker kontrolliert werden. Es können aber zwei Service Worker gleichzeitig installiert sein. Wenn Sie eine Änderung an Ihrem Service-Worker-Code vornehmen und die Seite aktualisieren, bearbeiten Sie Ihren Service-Worker eigentlich gar nicht. Service Worker sind unveränderlich. Stattdessen erstellen Sie einen brandneuen Service Worker. Dieser neue Service Worker (nennen wir ihn SW2) wird zwar installiert, aber noch nicht aktiviert. Er muss darauf warten, dass der aktuelle Service Worker (SW1) beendet wird (wenn Ihr Benutzer Ihre Website verlässt).
Mit den Caches eines anderen Service Workers spielen #
Während der Installation kann SW2 Dinge einrichten – normalerweise das Erstellen und Auffüllen von Caches. Aber Vorsicht: Dieser neue Service Worker hat Zugriff auf alles, worauf der aktuelle Service Worker Zugriff hat. Wenn Sie nicht aufpassen, kann Ihr neuer wartender Service-Worker Ihren aktuellen Service-Worker ganz schön durcheinander bringen. Einige Beispiele, die zu Problemen führen können:
- SW2 könnte einen Cache löschen, den SW1 aktiv nutzt.
- SW2 könnte den Inhalt eines Caches bearbeiten, den SW1 nutzt, und SW1 dazu veranlassen, mit Assets zu antworten, die die Seite nicht erwartet.
Skip skipWaiting #
Ein Service Worker kann auch die riskante Methode skipWaiting()
verwenden, um die Kontrolle über die Seite zu übernehmen, sobald sie mit der Installation fertig ist. Dies ist im Allgemeinen eine schlechte Idee, es sei denn, Sie versuchen absichtlich, einen fehlerhaften Service Worker zu ersetzen. Der neue Service-Worker könnte aktualisierte Ressourcen verwenden, die die aktuelle Seite nicht erwartet, was zu Fehlern und Bugs führt.
Sauber starten #
Der Weg, um zu verhindern, dass sich Ihre Service-Worker gegenseitig in die Quere kommen, besteht darin, sicherzustellen, dass sie verschiedene Caches verwenden. Der einfachste Weg, das zu erreichen, ist die Versionierung der Cache-Namen, die sie verwenden.
const version = 1;
const assetCacheName = `assets-${version}`;
self.addEventListener("install", event => {
caches.open(assetCacheName).then(cache => {
// confidently do stuff with your very own cache
});
});
Wenn Sie einen neuen Service-Worker einsetzen, werden Sie den version
stoßen, so dass er das, was er braucht, mit einem völlig anderen Cache als der vorherige Service-Worker tut.
End clean #
Wenn Ihr Service-Worker den Zustand activated
erreicht, wissen Sie, dass er übernommen hat und der vorherige Service-Worker redundant ist (d.h., nicht mehr benötigt). An diesem Punkt ist es wichtig, den alten Service Worker aufzuräumen. Das respektiert nicht nur die Cache-Speichergrenzen Ihrer Benutzer, sondern kann auch unbeabsichtigte Fehler verhindern.
Die caches.match()
-Methode ist eine häufig verwendete Abkürzung, um ein Element aus jedem Cache abzurufen, in dem es eine Übereinstimmung gibt. Sie durchläuft die Caches jedoch in der Reihenfolge, in der sie erstellt wurden. Nehmen wir also an, Sie haben zwei Versionen einer Skriptdatei app.js
in zwei verschiedenen Caches – assets-1
und assets-2
. Ihre Seite erwartet das neuere Skript, das in assets-2
gespeichert ist. Wenn Sie aber den alten Cache nicht gelöscht haben, wird caches.match('app.js')
das alte Skript aus assets-1
zurückgeben und höchstwahrscheinlich Ihre Website zerstören.
Alles, was man braucht, um nach den vorherigen Service Workern aufzuräumen, ist, jeden Cache zu löschen, den der neue Service Worker nicht braucht:
const version = 2;
const assetCacheName = `assets-${version}`;
self.addEventListener("activate", event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== assetCacheName){
return caches.delete(cacheName);
}
});
);
});
);
});
Verhindern, dass sich Ihre Service Worker gegenseitig in die Quere kommen, erfordert ein bisschen Arbeit und Disziplin, ist aber die Mühe wert.
Stellen Sie sich die Kombination aus Ihrem Service Worker und Ihrer Website als eine installierbare Anwendung vor. Jede Version sollte funktionieren. Jede Version sollte von den anderen getrennt sein. Stellen Sie sich vor, wie fehlerhaft ein Spiel wäre, wenn der Entwickler versehentlich einen Patch veröffentlichen würde, der neue Spiellogik, aber veraltete Assets verwendet. Sie würden in den Foren so schnell wütend werden! Halten Sie Ihre App-Versionen & sauber.
Mentalität der Service Worker #
Die richtige Einstellung zum Thema Service Worker wird Ihnen helfen, Ihre eigene mit Zuversicht aufzubauen. Sobald Sie den Dreh raus haben, werden Sie in der Lage sein, unglaubliche Erlebnisse für Ihre Benutzer zu schaffen.
Wenn Sie all dies anhand eines Spiels verstehen wollen, dann haben Sie Glück! Spiele Service Workies, wo du die Wege des Service Workers kennenlernst, um die Offline-Bestien zu erschlagen.