Das Leben eines Servicemitarbeiters

Ohne ihren Lebenszyklus lässt sich nur schwer herausfinden, was Service Worker tun. Ihr Innenleben wird undurchsichtig oder sogar willkürlich wirken. Denken Sie daran, dass das Verhalten von Service Workern – wie bei jeder anderen Browser-API – klar definiert und spezifiziert ist, und Offlineanwendungen möglich machen. Gleichzeitig können Updates durchgeführt werden, ohne die Nutzererfahrung zu beeinträchtigen.

Bevor Sie sich mit Workbox befassen, ist es wichtig, den Lebenszyklus des Service Workers zu verstehen, damit die Funktionsweise von Workbox sinnvoll ist.

Begriffe definieren

Bevor wir uns mit dem Service Worker-Lebenszyklus befassen, sollten Sie einige Begriffe zur Funktionsweise dieses Lebenszyklus definieren.

Kontrolle und Umfang

Das Konzept der Kontrolle ist entscheidend, um zu verstehen, wie Service Worker arbeiten. Eine Seite, die von einem Service Worker gesteuert wird, ist eine Seite, die es einem Service Worker ermöglicht, Netzwerkanfragen in seinem Namen abzufangen. Der Service Worker ist und in der Lage, im Rahmen eines bestimmten Bereichs für die Seite zu arbeiten.

Umfang

Der Bereich eines Service Workers wird durch seinen Standort auf einem Webserver bestimmt. Wenn ein Service Worker auf einer Seite unter /subdir/index.html ausgeführt wird und sich in /subdir/sw.js befindet, ist der Geltungsbereich des Service Workers /subdir/. Das Konzept des Umfangs wird in diesem Beispiel veranschaulicht:

  1. Rufen Sie https://service-worker-scope-viewer.glitch.me/subdir/index.html auf. Daraufhin wird eine Meldung angezeigt, dass die Seite von keinem Service Worker gesteuert wird. Auf dieser Seite wird jedoch ein Service Worker aus https://service-worker-scope-viewer.glitch.me/subdir/sw.js registriert.
  2. Lade die Seite neu. Da der Service Worker registriert wurde und jetzt aktiv ist, steuert er die Seite. Ein Formular mit dem Bereich, dem aktuellen Status und der URL des Service Workers ist sichtbar. Hinweis: Ein erneutes Laden der Seite hat nichts mit dem Bereich zu tun, sondern mit dem Service Worker-Lebenszyklus, der später erläutert wird.
  3. Gehen Sie jetzt zu https://service-worker-scope-viewer.glitch.me/index.html. Obwohl ein Service Worker für diesen Ursprung registriert wurde, wird eine Meldung angezeigt, dass derzeit kein Service Worker vorhanden ist. Das liegt daran, dass diese Seite nicht im Zuständigkeitsbereich des registrierten Service Workers liegt.

Der Bereich beschränkt, welche Seiten der Service Worker gesteuert. In diesem Beispiel bedeutet das, dass der aus /subdir/sw.js geladene Service Worker nur Seiten steuern kann, die sich in /subdir/ oder der Unterstruktur befinden.

Das obige Beispiel zeigt, wie der Umfang standardmäßig festgelegt wird. Der maximal zulässige Bereich kann jedoch überschrieben werden, indem der Service-Worker-Allowed-Antwortheader festgelegt und die Option scope an die Methode register übergeben wird.

Sofern es keinen guten Grund dafür gibt, den Service Worker-Bereich auf eine Teilmenge eines Ursprungs zu beschränken, laden Sie einen Service Worker aus dem Stammverzeichnis des Webservers, damit dessen Bereich so breit wie möglich ist. Machen Sie sich dabei keine Gedanken um den Service-Worker-Allowed-Header. So ist es für alle viel einfacher.

Kunde

Wenn von einem Service Worker eine Seite gesteuert wird, steuert er eigentlich einen Client. Als Client gilt jede geöffnete Seite, deren URL in den Bereich des Service Workers fällt. Im Einzelnen sind dies Instanzen eines WindowClient.

Der Lebenszyklus eines neuen Service Workers

Damit ein Service Worker eine Seite steuern kann, muss sie quasi erst erstellt werden. Beginnen wir damit, was passiert, wenn ein brandneuer Service Worker für eine Website ohne aktiven Service Worker bereitgestellt wird.

Anmeldung

Die Registrierung ist der erste Schritt des Service Worker-Lebenszyklus:

<!-- 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 tut Folgendes:

  1. Da der erste Besuch eines Nutzers auf einer Website ohne registrierten Service Worker stattfindet, sollten Sie mit der Registrierung eines Service Worker warten, bis die Seite vollständig geladen ist. Dadurch werden Bandbreitenkonflikte vermieden, falls der Service Worker etwas vorab im Cache speichert.
  2. Obwohl Service Worker gut unterstützt wird, hilft eine schnelle Überprüfung, Fehler in nicht unterstützten Browsern zu vermeiden.
  3. Wenn die Seite vollständig geladen ist und der Service Worker unterstützt wird, registrieren Sie /sw.js.

Hier sind einige wichtige Punkte, die Sie verstehen sollten:

  • Service Worker sind nur über HTTPS oder localhost verfügbar.
  • Wenn der Inhalt eines Service Workers Syntaxfehler enthält, schlägt die Registrierung fehl und der Service Worker wird verworfen.
  • Erinnerung: Service Worker arbeiten innerhalb eines Geltungsbereichs. Hier entspricht der Bereich dem gesamten Ursprung, da er aus dem Stammverzeichnis geladen wurde.
  • Zu Beginn der Registrierung wird der Service-Worker-Status auf 'installing' gesetzt.

Sobald die Registrierung abgeschlossen ist, beginnt die Installation.

Installation

Ein Service Worker löst nach der Registrierung sein Ereignis install aus. install wird nur einmal pro Service Worker aufgerufen und erst nach der Aktualisierung noch einmal ausgelöst. Ein Callback für das Ereignis install kann mit addEventListener im Bereich des Workers 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 vorab im Cache gespeichert. Wir werden später noch viel über das Precaching sprechen. Konzentrieren wir uns daher auf die Rolle von event.waitUntil. event.waitUntil nimmt ein Versprechen an und wartet, bis es aufgelöst wurde. In diesem Beispiel erfüllt dieses Promise zwei asynchrone Dinge:

  1. Erstellt eine neue Cache-Instanz mit dem Namen 'MyFancyCache_v1'.
  2. Nachdem der Cache erstellt wurde, wird ein Array von Asset-URLs mithilfe der asynchronen addAll-Methode vorab im Cache gespeichert.

Die Installation schlägt fehl, wenn die an event.waitUntil übergebenen Promise abgelehnt werden. In diesem Fall wird der Service Worker verworfen.

Wenn resolve verspricht, ist die Installation erfolgreich. Der Status des Service Workers ändert sich in 'installed' und wird aktiviert.

Aktivierung

Wenn die Registrierung und Installation erfolgreich sind, wird der Service Worker aktiviert und erhält den Status 'activating'. Die Arbeit kann während der Aktivierung im activate-Ereignis des Service Workers ausgeführt werden. Eine typische Aufgabe bei diesem Ereignis besteht darin, alte Caches zu bereinigen, aber für einen ganz neuen Service Worker ist dies im Moment nicht relevant. Wir kommen darauf zurück, wenn wir über Service Worker-Updates sprechen.

Bei neuen Service Workern wird activate sofort ausgelöst, nachdem install erfolgreich war. Nach Abschluss der Aktivierung ändert sich der Status des Service Workers zu 'activated'. Beachten Sie, dass der neue Service Worker die Seite standardmäßig erst bei der nächsten Navigation oder Seitenaktualisierung steuert.

Service Worker-Updates verarbeiten

Nachdem der erste Service Worker bereitgestellt wurde, muss er wahrscheinlich später aktualisiert werden. Beispielsweise kann eine Aktualisierung erforderlich sein, wenn Änderungen an der Anfrageverarbeitungs- oder Precaching-Logik auftreten.

Wann werden Updates durchgeführt?

Browser suchen in folgenden Fällen nach Updates für einen Service Worker:

  • Der Nutzer ruft eine Seite innerhalb des Service Worker-Bereichs auf.
  • navigator.serviceWorker.register() wird mit einer anderen URL als der derzeit installierten Service Worker aufgerufen. Ändern Sie jedoch nicht die URL eines Service Workers.
  • navigator.serviceWorker.register() wird mit der gleichen URL wie der installierte Service Worker aufgerufen, jedoch mit einem anderen Bereich. Vermeiden Sie dies wieder, indem Sie den Bereich nach Möglichkeit im Stamm eines Ursprungs belassen.
  • Wenn Ereignisse wie 'push' oder 'sync' in den letzten 24 Stunden ausgelöst wurden. Sie müssen sich jedoch noch keine Gedanken über diese Ereignisse machen.

Wie werden Updates durchgeführt?

Wichtig ist zu wissen, wann der Browser einen Service Worker aktualisiert. Dies gilt jedoch auch für das „Wie“. Wenn die URL oder der Bereich eines Service Workers unverändert ist, wird ein aktuell installierter Service Worker nur dann auf eine neue Version aktualisiert, wenn sich sein Inhalt geändert hat.

Browser erkennen Änderungen auf unterschiedliche Weise:

  • Alle Byte für Byte an Skripts, die von importScripts angefordert werden, sofern zutreffend.
  • Alle Änderungen am Top-Level-Code des Service Workers, die sich auf den vom Browser generierten Fingerabdruck auswirken.

Der Browser übernimmt hier sehr viel Arbeit. Um sicherzustellen, dass der Browser alles hat, was erforderlich ist, um Änderungen an den Inhalten eines Service Workers zuverlässig zu erkennen, sollten Sie den HTTP-Cache nicht anweisen, diese zu speichern, und den Dateinamen nicht ändern. Der Browser führt automatisch Updateprüfungen durch, wenn innerhalb des Geltungsbereichs eines Service Workers eine neue Seite aufgerufen wird.

Updateprüfungen manuell auslösen

In Bezug auf Updates sollte sich die Registrierungslogik im Allgemeinen nicht ändern. Eine Ausnahme könnte jedoch auftreten, wenn Sitzungen auf einer Website langlebig sind. Dies kann in Single-Page-Anwendungen vorkommen, bei denen Navigationsanfragen selten sind, da die Anwendung in der Regel zu Beginn des Lebenszyklus der Anwendung auf eine einzige Navigationsanfrage stößt. In solchen Situationen kann im Hauptthread ein manuelles Update ausgelöst werden:

navigator.serviceWorker.ready.then((registration) => {
  registration.update();
});

Bei herkömmlichen Websites oder in Fällen, in denen Nutzersitzungen nicht langlebig sind, ist das Auslösen manueller Aktualisierungen wahrscheinlich nicht erforderlich.

Installation

Wenn Sie einen Bundler zum Generieren statischer Assets verwenden, enthalten diese Assets Hashes im Namen, z. B. framework.3defa9d2.js. Angenommen, einige dieser Assets werden vorab für den Offlinezugriff im Cache gespeichert. Dies würde eine Aktualisierung durch einen Service Worker erfordern, um aktualisierte 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'
    ]);
  }));
});

Zwei Dinge unterscheiden sich vom ersten install-Ereignisbeispiel von oben:

  1. Eine neue Cache-Instanz mit dem Schlüssel 'MyFancyCacheName_v2' wird erstellt.
  2. Die Namen der vorab im Cache gespeicherten Assets haben sich geändert.

Zu beachten ist, dass ein aktualisierter Service Worker zusammen mit dem vorherigen installiert wird. Das bedeutet, dass der alte Service Worker weiterhin alle geöffneten Seiten verwaltet und nach der Installation der neue Service Worker bis zu seiner Aktivierung in den Wartestatus versetzt wird.

Standardmäßig wird ein neuer Service Worker aktiviert, wenn über den alten Service keine Clients mehr gesteuert werden. Dies geschieht, wenn alle geöffneten Tabs für die relevante Website geschlossen sind.

Aktivierung

Wenn ein aktualisierter Service Worker installiert wird und die Wartezeit endet, wird er aktiviert und der alte Service Worker verworfen. Häufig werden beim activate-Ereignis eines aktualisierten Service Workers alte Caches bereinigt. Entfernen Sie alte Caches. Rufen Sie dazu die Schlüssel für alle offenen Cache-Instanzen mit caches.keys ab und löschen Sie Caches, 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 räumen nicht auf. Wir müssen das selbst tun, sonst riskieren wir, das Speicherkontingent zu überschreiten. Da die 'MyFancyCacheName_v1' des ersten Service Workers veraltet ist, wird die Cache-Zulassungsliste mit 'MyFancyCacheName_v2' aktualisiert. Dadurch werden Caches mit einem anderen Namen gelöscht.

Das Ereignis activate wird beendet, nachdem der alte Cache entfernt wurde. Jetzt übernimmt der neue Service Worker die Kontrolle über die Seite und ersetzt schließlich die alte.

Der Lebenszyklus geht immer weiter

Unabhängig davon, ob Workbox die Bereitstellung und Aktualisierung von Service Workern verarbeitet oder die Service Worker API direkt verwendet, lohnt es sich, den Lebenszyklus des Service Workers nachzuvollziehen. Mit diesem Verständnis dürfte das Verhalten von Service Workern eher logisch als mysteriös erscheinen.

Wenn Sie sich ausführlicher mit diesem Thema befassen möchten, sollten Sie sich diesen Artikel von Jake Archibald ansehen. Es gibt viele Nuancen in der Art und Weise, wie der gesamte Dienstlebenszyklus verläuft, aber es ist bekannt und dieses Wissen reicht bei der Verwendung von Workbox.