Wie Sie über Dienstprogramme denken sollten.
Dienstprogramme sind leistungsstark und es lohnt sich, sie zu lernen. Sie ermöglichen eine völlig neue Nutzererfahrung. Ihre Website kann sofort geladen werden. Sie kann offline verwendet werden. Sie kann als plattformspezifische App installiert werden und ist genauso ausgefeilt – aber mit der Reichweite und Freiheit des Webs.
Service Worker sind jedoch anders als alles, was die meisten Webentwickler gewohnt sind. Sie haben eine steile Lernkurve und einige Stolpersteine, auf die Sie achten müssen.
Google Developers und ich haben vor Kurzem an einem Projekt namens Service Workies zusammengearbeitet. Mit diesem kostenlosen Spiel möchten wir Service Worker besser verstehen. Beim Erstellen und Arbeiten mit den komplexen Details von Service Workers stieß ich auf einige Probleme. Am meisten hat mir geholfen, ein paar beschreibende Metaphern zu entwickeln. In diesem Beitrag werden wir uns diese mentalen Modelle ansehen und uns mit den paradoxen Eigenschaften auseinandersetzen, die Servicemitarbeiter sowohl schwierig als auch großartig machen.
Dasselbe, aber anders
Beim Codieren Ihres Dienstarbeiters werden Ihnen viele Dinge bekannt vorkommen. Sie können Ihre bevorzugten neuen JavaScript-Sprachfunktionen verwenden. Lebenszyklusereignisse werden genau wie UI-Ereignisse überwacht. Sie steuern die Ablaufsteuerung wie gewohnt mit Promises.
Aber bei anderen Service Worker-Verhaltensweisen können Sie nur den Kopf schütteln. Das ist besonders dann der Fall, wenn Sie die Seite aktualisieren und Ihre Codeänderungen nicht angewendet werden.
Eine neue Ebene
Normalerweise müssen Sie beim Erstellen einer Website nur zwei Ebenen berücksichtigen: den Client und den Server. Der Service Worker ist eine brandneue Schicht, die sich in der Mitte befindet.
Stellen Sie sich Ihren Service Worker als eine Art Browsererweiterung vor, die Ihre Website im Browser des Nutzers installieren kann. Nach der Installation erweitert der Service Worker den Browser für Ihre Website um eine leistungsstarke mittlere Ebene. Diese Service Worker-Ebene kann alle Anfragen Ihrer Website abfangen und verarbeiten.
Die Dienstworker-Ebene hat einen eigenen Lebenszyklus, der unabhängig vom Browsertab ist. Ein einfacher Seitenaktualisierungsvorgang reicht nicht aus, um einen Service Worker zu aktualisieren. Genauso wenig würden Sie erwarten, dass durch eine Seitenaktualisierung Code aktualisiert wird, der auf einem Server bereitgestellt wird. Für jede Ebene gelten eigene Aktualisierungsregeln.
Im Spiel Service Workies erfahren Sie alles über den Lebenszyklus von Dienstprogrammen und können viel Praxiserfahrung sammeln.
Leistungsstark, aber begrenzt
Ein Service Worker auf Ihrer Website bietet Ihnen unglaubliche Vorteile. Ihre Website kann:
- auch dann einwandfrei funktionieren, wenn der Nutzer offline ist
- erhebliche Leistungsverbesserungen durch Caching
- Push-Benachrichtigungen verwenden
- als PWA installiert werden.
Service Worker sind zwar sehr leistungsfähig, aber auch durch ihr Design eingeschränkt. Sie können nichts synchron oder im selben Thread wie Ihre Website tun. Das bedeutet, dass Sie keinen Zugriff auf Folgendes haben:
- localStorage
- DOM
- das Fenster
Die gute Nachricht ist, dass es einige Möglichkeiten gibt, wie Ihre Seite mit dem Service Worker kommunizieren kann, z. B. postMessage
, 1:1-Nachrichtenkanäle und 1:n-Broadcast-Kanäle.
Langlebig, aber kurzlebig
Ein aktiver Service Worker bleibt auch dann aktiv, wenn ein Nutzer Ihre Website verlässt oder den Tab schließt. Der Browser hält diesen Service Worker bei, damit er bereit ist, wenn der Nutzer das nächste Mal Ihre Website besucht. Vor der allerersten Anfrage erhält der Service Worker die Möglichkeit, die Seite abzufangen und die Kontrolle über die Seite zu übernehmen. So kann eine Website auch offline funktionieren: Der Service Worker kann eine im Cache gespeicherte Version der Seite selbst bereitstellen, auch wenn der Nutzer keine Internetverbindung hat.
In Service Workies wird dieses Konzept mit Kolohe (einem freundlichen Dienstarbeiter) veranschaulicht, der Anfragen abfängt und verarbeitet.
Angehalten
Auch wenn Service Worker unsterblich erscheinen, können sie fast jederzeit angehalten werden. Der Browser möchte keine Ressourcen für einen Service Worker verschwenden, der derzeit nichts tut. Ein Stopp ist nicht dasselbe wie eine Beendigung – der Dienst-Worker bleibt installiert und aktiviert. Er wird nur in den Ruhemodus versetzt. Wenn sie das nächste Mal benötigt wird (z. B. zum Ausführen einer Anfrage), wird sie vom Browser wieder aktiviert.
waitUntil
Da der Dienst-Worker jederzeit in den Ruhemodus versetzt werden kann, muss er dem Browser mitteilen können, wenn er gerade etwas Wichtiges tut und nicht in den Ruhemodus versetzt werden möchte. Hier kommt event.waitUntil()
ins Spiel. Diese Methode erweitert den Lebenszyklus, in dem sie verwendet wird, sodass sie nicht beendet wird, sondern auch erst dann in die nächste Phase des Lebenszyklus übergeht, bis sie bereit ist. So haben wir Zeit, Caches einzurichten, Ressourcen aus dem Netzwerk abzurufen usw.
Dieses Beispiel teilt dem Browser mit, dass der Service Worker die Installation erst abgeschlossen hat, wenn der assets
-Cache erstellt und mit dem Bild eines Schwerts gefüllt wurde:
self.addEventListener("install", event => {
event.waitUntil(
caches.open("assets").then(cache => {
return cache.addAll(["/weapons/sword/blade.png"]);
})
);
});
Achten Sie auf den globalen Zustand
Bei diesem Start/Stopp wird der globale Gültigkeitsbereich des Dienstarbeiters zurückgesetzt. Verwenden Sie also keinen globalen Status in Ihrem Service Worker, da er sonst beim nächsten Starten einen anderen Status als erwartet haben könnte.
Betrachten Sie dieses Beispiel mit einem globalen Status:
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, z. B. 0.13981866382421893
. Die Variable hasHandledARequest
ändert sich ebenfalls in true
. Nun ist der Dienstworker eine Weile inaktiv, sodass der Browser ihn beendet. Wenn das nächste Mal eine Anfrage erfolgt, wird der Service Worker wieder benötigt und der Browser weckt ihn auf. Das Script wird erneut ausgewertet. Jetzt ist hasHandledARequest
auf false
zurückgesetzt und favoriteNumber
ist etwas ganz anderes: 0.5907281835659033
.
Sie können sich nicht auf den gespeicherten Status in einem Service Worker verlassen. Außerdem kann das Erstellen von Instanzen von Elementen wie Nachrichtenkanälen zu Fehlern führen: Jedes Mal, wenn der Dienstarbeiter angehalten oder gestartet wird, wird eine brandneue Instanz erstellt.
In Kapitel 3 wird der angehaltene Dienst-Worker als farblos dargestellt, während er darauf wartet, geweckt zu werden.
Zusammen, aber getrennt
Ihre Seite kann jeweils nur von einem Service Worker gesteuert werden. Es können jedoch zwei Service Worker gleichzeitig installiert sein. Wenn Sie Ihren Service Worker-Code ändern und die Seite aktualisieren, wird der Service Worker nicht tatsächlich bearbeitet. Dienstprogramme sind unveränderlich. Stattdessen erstellen Sie ein ganz neues. Dieser neue Dienst-Worker (nennen wir ihn SW2) wird installiert, aber noch nicht aktiviert. Er muss warten, bis der aktuelle Service Worker (SW1) beendet wird, also wenn der Nutzer Ihre Website verlässt.
Caches anderer Service Worker manipulieren
Während der Installation kann SW2 die Einrichtung übernehmen, in der Regel durch Erstellen und Befüllen von Caches. Hinweis: Dieser neue Dienst-Worker hat Zugriff auf alles, auf das der aktuelle Dienst-Worker Zugriff hat. Wenn Sie nicht vorsichtig sind, kann der neue wartende Service Worker Ihrem aktuellen Service Worker das Leben schwer machen. Beispiele für Probleme:
- SW2 könnte einen Cache löschen, den SW1 aktiv verwendet.
- SW2 könnte den Inhalt des von SW1 verwendeten Caches bearbeiten, sodass SW1 mit Assets antwortet, die die Seite nicht erwartet.
ÜberspringenSkipWarten
Ein Service Worker kann auch die riskante Methode skipWaiting()
verwenden, um die Kontrolle über die Seite zu übernehmen, sobald die Installation abgeschlossen ist. Das ist in der Regel keine gute Idee, es sei denn, Sie versuchen absichtlich, einen fehlerhaften Service Worker zu ersetzen. Der neue Dienst-Worker verwendet möglicherweise aktualisierte Ressourcen, die von der aktuellen Seite nicht erwartet werden, was zu Fehlern führt.
Mit einem sauberen System beginnen
Sie können verhindern, dass Ihre Service Worker sich gegenseitig überschreiben, indem Sie dafür sorgen, dass sie unterschiedliche Caches verwenden. Am einfachsten geht dies, indem Sie die verwendeten Cache-Namen versionieren.
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 bereitstellen, übertragen Sie den version
so, dass er das tut, was er braucht, und zwar mit einem vollständig vom vorherigen Service Worker getrennten Cache.
Endreinigung
Sobald Ihr Service Worker den Status activated
erreicht, wissen Sie, dass er übernommen wurde und der vorherige Service Worker redundant ist. An dieser Stelle ist es wichtig, die Bereinigung nach dem alten Service Worker durchzuführen. Dabei werden nicht nur die Cache-Speicherlimits der Nutzer berücksichtigt, sondern auch unbeabsichtigte Programmfehler verhindert.
Die Methode caches.match()
ist eine häufig verwendete Abkürzung zum Abrufen eines Elements aus einem beliebigen Cache, bei dem eine Übereinstimmung vorliegt. Die Caches werden jedoch in der Reihenfolge durchgegangen, in der sie erstellt wurden. Angenommen, Sie haben zwei Versionen einer Scriptdatei app.js
in zwei verschiedenen Caches: assets-1
und assets-2
. Deine Seite erwartet das neuere Skript, das in assets-2
gespeichert ist. Wenn du den alten Cache allerdings nicht gelöscht hast, gibt caches.match('app.js')
den alten von assets-1
zurück, wodurch deine Website höchstwahrscheinlich beschädigt wird.
Zum Bereinigen der vorherigen Service Worker müssen Sie nur den Cache löschen, den der neue Service Worker nicht benötigt:
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);
}
});
);
});
);
});
Es erfordert etwas Arbeit und Disziplin, zu verhindern, dass sich Ihre Service Worker gegenseitig blockieren. Der Aufwand lohnt sich aber.
Service Worker-Mindset
Wenn Sie sich mit der richtigen Einstellung beschäftigen, während Sie über Service Worker nachdenken, können Sie Ihr Unternehmen mit Selbstvertrauen aufbauen. Sobald Sie sich mit ihnen vertraut gemacht haben, können Sie beeindruckende Erlebnisse für Ihre Nutzer schaffen.
Wenn Sie all das durch Spielen verstehen möchten, haben Sie Glück! Spielen Sie Service Workies, um die Geheimnisse von Service Workern zu erfahren und die Offline-Monster zu besiegen.