Ohne den Lebenszyklus von Dienst-Workern zu kennen, ist es schwierig zu verstehen, was sie tun. Ihre Funktionsweise erscheint undurchsichtig, ja willkürlich. Wie bei jeder anderen Browser-API sind das Verhalten von Dienst-Workern klar definiert und spezifiziert. Sie ermöglichen Offlineanwendungen und erleichtern Updates, ohne die Nutzerfreundlichkeit zu beeinträchtigen.
Bevor Sie sich mit Workbox befassen, sollten Sie den Dienstworker-Lebenszyklus kennen, damit Sie die Funktionsweise von Workbox nachvollziehen können.
Begriffe definieren
Bevor wir uns mit dem Service Worker-Lebenszyklus befassen, sollten wir einige Begriffe definieren, die sich auf die Funktionsweise dieses Lebenszyklus beziehen.
Kontrolle und Umfang
Das Konzept der Steuerung ist entscheidend, um zu verstehen, wie Dienstprogramme funktionieren. Eine Seite, die als von einem Service Worker gesteuert beschrieben wird, ist eine Seite, auf der ein Service Worker Netzwerkanfragen in seinem Namen abfangen kann. Der Service Worker ist vorhanden und kann Aufgaben für die Seite innerhalb eines bestimmten Bereichs ausführen.
Umfang
Der Umfang eines Dienstarbeiters wird durch seinen Speicherort auf einem Webserver bestimmt.
Wenn ein Service Worker auf einer Seite mit der URL /subdir/index.html
ausgeführt wird und sich selbst unter /subdir/sw.js
befindet, ist /subdir/
sein Zuständigkeitsbereich.
Hier ein Beispiel für das Konzept „Umfang“:
- Rufen Sie https://service-worker-scope-viewer.glitch.me/subdir/index.html auf.
Es wird eine Meldung angezeigt, dass die Seite von keinem Service Worker gesteuert wird.
Auf dieser Seite wird jedoch ein Service Worker von
https://service-worker-scope-viewer.glitch.me/subdir/sw.js
registriert. - Lade die Seite neu. Da der Service Worker registriert und jetzt aktiv ist, steuert er die Seite. Ein Formular mit dem Umfang, dem aktuellen Status und der URL des Dienstarbeiters wird angezeigt. Hinweis: Das Aktualisieren der Seite hat nichts mit dem Gültigkeitsbereich zu tun, sondern mit dem Lebenszyklus des Service Workers, der später erläutert wird.
- Rufen Sie jetzt https://service-worker-scope-viewer.glitch.me/index.html auf. Obwohl für diesen Ursprung ein Service Worker registriert wurde, wird weiterhin die Meldung angezeigt, dass derzeit kein Service Worker vorhanden ist. Das liegt daran, dass sich diese Seite nicht im Zuständigkeitsbereich des registrierten Service Workers befindet.
Mit dem Umfang wird festgelegt, welche Seiten der Service Worker steuert.
In diesem Beispiel bedeutet das, dass der von /subdir/sw.js
geladene Dienstarbeiter nur Seiten in /subdir/
oder seinem untergeordneten Knoten steuern kann.
So funktioniert die Begrenzung standardmäßig. Der maximal zulässige Umfang kann jedoch überschrieben werden, indem Sie den Antwortheader Service-Worker-Allowed
festlegen und der register
-Methode eine scope
-Option übergeben.
Laden Sie einen Service Worker aus dem Stammverzeichnis des Webservers, damit sein Umfang so breit wie möglich ist, es sei denn, es gibt einen triftigen Grund, den Umfang des Service Workers auf einen Teil eines Ursprungs zu beschränken. Machen Sie sich keine Gedanken über den Service-Worker-Allowed
-Header. So ist es für alle viel einfacher.
Kunde
Wenn gesagt wird, dass ein Service Worker eine Seite steuert, steuert er in Wirklichkeit einen Client.
Ein Client ist jede geöffnete Seite, deren URL in den Zuständigkeitsbereich dieses Service Workers fällt.
Dabei handelt es sich um Instanzen einer WindowClient
.
Lebenszyklus eines neuen Dienstarbeiters
Damit ein Dienstarbeiter eine Seite steuern kann, muss er erst erstellt werden. Sehen wir uns zuerst an, was passiert, wenn ein brandneuer Service Worker für eine Website ohne aktiven Service Worker bereitgestellt wird.
Anmeldung
Die Registrierung ist der erste Schritt im Lebenszyklus eines Service Workers:
<!-- In index.html, for example: -->
<script>
// Don't register the service worker
// until the page has fully loaded
window.addEventListener('load', () => {
// Is service worker available?
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(() => {
console.log('Service worker registered!');
}).catch((error) => {
console.warn('Error registering service worker:');
console.warn(error);
});
}
});
</script>
Dieser Code wird im Hauptthread ausgeführt und führt Folgendes aus:
- Da der erste Besuch einer Website durch den Nutzer ohne registrierten Service Worker erfolgt, warten Sie, bis die Seite vollständig geladen ist, bevor Sie einen registrieren. So wird eine Bandbreitenauslastung vermieden, wenn der Service Worker etwas im Voraus im Cache speichert.
- Service Worker werden zwar gut unterstützt, aber eine kurze Prüfung hilft, Fehler in Browsern zu vermeiden, in denen sie nicht unterstützt werden.
- Wenn die Seite vollständig geladen ist und Service Worker unterstützt wird, registrieren Sie
/sw.js
.
Folgendes ist wichtig:
- Dienstprogramme sind nur über HTTPS oder localhost verfügbar.
- Wenn der Inhalt eines Dienstarbeiters Syntaxfehler enthält, schlägt die Registrierung fehl und der Dienstarbeiter wird verworfen.
- Zur Erinnerung: Service Worker arbeiten innerhalb eines Gültigkeitsbereichs. Hier ist der gesamte Ursprung der Umfang, da er aus dem Stammverzeichnis geladen wurde.
- Zu Beginn der Registrierung wird der Status des Dienst-Workers auf
'installing'
gesetzt.
Nach Abschluss der Registrierung beginnt die Installation.
Installation
Ein Service Worker löst das Ereignis install
nach der Registrierung aus.
install
wird nur einmal pro Service Worker aufgerufen und erst wieder ausgelöst, wenn er aktualisiert wurde.
Ein Callback für das Ereignis install
kann im Bereich des Workers mit addEventListener
registriert werden:
// /sw.js
self.addEventListener('install', (event) => {
const cacheKey = 'MyFancyCacheName_v1';
event.waitUntil(caches.open(cacheKey).then((cache) => {
// Add all the assets in the array to the 'MyFancyCacheName_v1'
// `Cache` instance for later use.
return cache.addAll([
'/css/global.bc7b80b7.css',
'/css/home.fe5d0b23.css',
'/js/home.d3cc4ba4.js',
'/js/jquery.43ca4933.js'
]);
}));
});
Dadurch wird eine neue Cache
-Instanz erstellt und Assets werden vorab im Cache gespeichert.
Wir haben später noch viele Gelegenheiten, über das Precaching zu sprechen. Konzentrieren wir uns jetzt auf die Rolle von event.waitUntil
. event.waitUntil
nimmt ein Versprechen an und wartet, bis es erfüllt wurde.
In diesem Beispiel führt dieses Versprechen zwei asynchrone Aktionen aus:
- Erstellt eine neue
Cache
-Instanz mit dem Namen'MyFancyCache_v1'
. - Nach dem Erstellen des Caches wird ein Array von Asset-URLs mit der asynchronen
addAll
-Methode vorab im Cache gespeichert.
Die Installation schlägt fehl, wenn die an event.waitUntil
übergebenen Versprechen abgelehnt werden.
In diesem Fall wird der Dienstarbeiter verworfen.
Wenn die Versprechen erfüllt werden, ist die Installation erfolgreich und der Status des Service Workers ändert sich zu 'installed'
. Er wird dann aktiviert.
Aktivierung
Wenn die Registrierung und Installation erfolgreich sind, wird der Service Worker aktiviert und sein Status ändert sich in 'activating'
. Während der Aktivierung können im Ereignis activate
des Service Workers Aufgaben ausgeführt werden.
Eine typische Aufgabe bei diesem Ereignis besteht darin, alte Caches zu bereinigen. Bei einem brandneuen Service Worker ist das jedoch momentan nicht relevant. Wir werden darauf später bei den Service Worker-Updates noch näher eingehen.
Bei neuen Dienstmitarbeitern wird activate
sofort ausgelöst, nachdem install
erfolgreich war.
Nach Abschluss der Aktivierung ändert sich der Status des Dienstarbeiters in 'activated'
.
Der neue Service Worker steuert die Seite standardmäßig erst bei der nächsten Navigation oder Seitenaktualisierung.
Umgang mit Service Worker-Updates
Nachdem der erste Service Worker bereitgestellt wurde, muss er wahrscheinlich später aktualisiert werden. Ein Update kann beispielsweise erforderlich sein, wenn Änderungen an der Anfrageverarbeitung oder der Logik für das Vorab-Caching vorgenommen werden.
Wann Updates durchgeführt werden
Browser suchen nach Updates für einen Dienst-Worker in folgenden Fällen:
- Der Nutzer ruft eine Seite auf, die sich im Zuständigkeitsbereich des Service Workers befindet.
navigator.serviceWorker.register()
wird mit einer URL aufgerufen, die sich von der des aktuell installierten Dienstarbeiters unterscheidet. Ändern Sie jedoch nicht die URL eines Dienstarbeiters.navigator.serviceWorker.register()
wird mit derselben URL wie der installierte Service Worker, aber mit einem anderen Gültigkeitsbereich aufgerufen. Vermeiden Sie dies nach Möglichkeit, indem Sie den Umfang auf die Wurzel eines Ursprungs beschränken.- Wenn Ereignisse wie
'push'
oder'sync'
in den letzten 24 Stunden ausgelöst wurden. Machen Sie sich aber noch keine Sorgen.
So werden Updates durchgeführt
Es ist wichtig zu wissen, wann der Browser einen Dienst-Worker aktualisiert, aber auch, wie. Angenommen, die URL oder der Bereich eines Service Workers bleibt unverändert, wird ein derzeit installierter Service Worker nur dann auf eine neue Version aktualisiert, wenn sich seine Inhalte geändert haben.
Browser erkennen Änderungen auf verschiedene Arten:
- Alle Byte für Byte-Änderungen an Scripts, die von
importScripts
angefordert wurden, falls zutreffend. - Alle Änderungen am Code der obersten Ebene des Dienstarbeiters, die sich auf den vom Browser generierten Fingerabdruck auswirken.
Der Browser übernimmt hier einen Großteil der Arbeit. Damit der Browser alles hat, was er zum zuverlässigen Erkennen von Änderungen am Inhalt eines Dienst-Workers benötigt, sollten Sie den HTTP-Cache nicht anweisen, die Datei zu behalten, und den Dateinamen nicht ändern. Der Browser führt automatisch Aktualisierungsüberprüfungen durch, wenn eine Navigation zu einer neuen Seite innerhalb des Gültigkeitsbereichs eines Dienstarbeiters erfolgt.
Manuelle Suche nach Updates
Die Registrierungslogik sollte sich durch Updates im Allgemeinen nicht ändern. Eine Ausnahme kann jedoch vorliegen, wenn Sitzungen auf einer Website lang andauern. Das kann bei Single-Page-Anwendungen passieren, bei denen Navigationsanfragen selten sind, da die Anwendung zu Beginn des Lebenszyklus der Anwendung in der Regel eine Navigationsanfrage erhält. In solchen Fällen kann eine manuelle Aktualisierung im Hauptthread ausgelöst werden:
navigator.serviceWorker.ready.then((registration) => {
registration.update();
});
Bei herkömmlichen Websites oder in anderen Fällen, in denen Nutzersitzungen nicht lange andauern, ist das Auslösen manueller Aktualisierungen wahrscheinlich nicht erforderlich.
Installation
Wenn Sie mit einem Bundler statische Assets generieren, enthalten diese Assets Hash-Werte im Namen, z. B. framework.3defa9d2.js
.
Angenommen, einige dieser Assets werden vorab für den Offlinezugriff zwischengespeichert.
Dazu ist ein Service Worker-Update erforderlich, um die aktualisierten Assets vorab im Cache zu speichern:
self.addEventListener('install', (event) => {
const cacheKey = 'MyFancyCacheName_v2';
event.waitUntil(caches.open(cacheKey).then((cache) => {
// Add all the assets in the array to the 'MyFancyCacheName_v2'
// `Cache` instance for later use.
return cache.addAll([
'/css/global.ced4aef2.css',
'/css/home.cbe409ad.css',
'/js/home.109defa4.js',
'/js/jquery.38caf32d.js'
]);
}));
});
Im Vergleich zum ersten install
-Ereignisbeispiel gibt es zwei Unterschiede:
- Es wird eine neue
Cache
-Instanz mit dem Schlüssel'MyFancyCacheName_v2'
erstellt. - Die Namen der vorab im Cache gespeicherten Assets haben sich geändert.
Beachten Sie, dass ein aktualisierter Service Worker neben dem vorherigen installiert wird. Das bedeutet, dass der alte Service Worker weiterhin für alle geöffneten Seiten zuständig ist. Nach der Installation befindet sich der neue Service Worker in einem Wartestatus, bis er aktiviert wird.
Standardmäßig wird ein neuer Dienst-Worker aktiviert, wenn keine Clients mehr vom alten gesteuert werden. Das passiert, wenn alle geöffneten Tabs für die entsprechende Website geschlossen werden.
Aktivierung
Wenn ein aktualisierter Dienst-Worker installiert wurde und die Wartephase endet, wird er aktiviert und der alte Dienst-Worker wird verworfen.
Eine häufige Aufgabe im activate
-Ereignis eines aktualisierten Service Workers besteht darin, alte Caches zu bereinigen.
Entfernen Sie alte Caches, indem Sie die Schlüssel für alle offenen Cache
-Instanzen mit caches.keys
abrufen und Caches löschen, die nicht in einer definierten Zulassungsliste mit caches.delete
enthalten sind:
self.addEventListener('activate', (event) => {
// Specify allowed cache keys
const cacheAllowList = ['MyFancyCacheName_v2'];
// Get all the currently active `Cache` instances.
event.waitUntil(caches.keys().then((keys) => {
// Delete all caches that aren't in the allow list:
return Promise.all(keys.map((key) => {
if (!cacheAllowList.includes(key)) {
return caches.delete(key);
}
}));
}));
});
Alte Caches werden nicht automatisch bereinigt.
Andernfalls besteht die Gefahr, dass wir das Speicherkontingent überschreiten.
Da 'MyFancyCacheName_v1'
aus dem ersten Service Worker veraltet ist, wird die Cache-Zulassungsliste aktualisiert, um 'MyFancyCacheName_v2'
anzugeben. Dadurch werden Caches mit einem anderen Namen gelöscht.
Das activate
-Ereignis wird beendet, nachdem der alte Cache entfernt wurde.
An diesem Punkt übernimmt der neue Service Worker die Kontrolle über die Seite und ersetzt den alten.
Der Lebenszyklus geht weiter
Unabhängig davon, ob Workbox für die Bereitstellung und Aktualisierung von Service Workern verwendet wird oder die Service Worker API direkt verwendet wird, ist es wichtig, den Service Worker-Lebenszyklus zu verstehen. Mit diesem Wissen sollte das Verhalten von Dienstarbeitern logischer und weniger mysteriös erscheinen.
Weitere Informationen zu diesem Thema finden Sie in diesem Artikel von Jake Archibald. Der Dienstlebenszyklus ist sehr komplex, aber er kann erlernt werden. Dieses Wissen ist bei der Verwendung von Workbox sehr hilfreich.