Service Worker-Mentalität

Denken Sie an Service Worker.

Geddes
Dave Geddes

Service Worker sind leistungsstark und es lohnt sich, es zu lernen. Sie ermöglichen es Ihnen, Ihren Nutzern ein völlig neues Erlebnis zu bieten. Ihre Website kann sofort geladen werden. Es funktioniert auch offline. Sie kann als plattformspezifische App installiert werden und wirkt ein wenig ausgefeilt, aber mit der Reichweite und Freiheit des Webs.

Service Worker sind jedoch anders als alles, was die meisten von uns Webentwicklern gewohnt sind. Sie bringen eine steile Lernkurve mit sich und es gibt ein paar Hindernisse, auf die Sie achten müssen.

Google Developers und ich haben vor Kurzem an dem Projekt Service Workies mitgearbeitet, einem kostenlosen Spiel zum Verständnis von Service Workern. Bei der Konstruktion und bei der Arbeit mit den komplexen Vor- und Nachteilen der Service Worker stieß ich auf ein paar Probleme. Was mir am meisten geholfen hat, war die Entwicklung einiger bildlicher Metaphern. In diesem Beitrag sehen wir uns diese mentalen Modelle an und beschäftigen uns mit den paradoxen Eigenschaften, die Service Workers sowohl schwierig als auch genial machen.

Gleich, aber anders

Beim Programmieren Ihres Service Workers werden Ihnen viele Dinge vertraut vorkommen. Sie können Ihre bevorzugten neuen JavaScript-Sprachfunktionen verwenden. Lebenszyklusereignisse werden genau wie UI-Ereignisse überwacht. Du verwaltest den Kontrollablauf mit Versprechen, wie du es gewohnt bist.

Andere Verhaltensweisen von Service Workern führen jedoch dazu, dass Sie sich verwirrt fühlen. Das gilt vor allem dann, wenn Sie die Seite aktualisieren und die Änderungen am Code nicht übernommen werden.

Eine neue Ebene

Normalerweise müssen Sie beim Erstellen einer Website nur über zwei Ebenen nachdenken: den Client und den Server. Der Service Worker ist eine brandneue Ebene, die sich in der Mitte befindet.

Ein Service Worker fungiert als mittlere Ebene zwischen Client und Server

Stellen Sie sich Ihren Service Worker als eine Art Browsererweiterung vor, die Ihre Website in den Browsern der Nutzer installieren kann. Nach der Installation extends 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 Service Worker-Ebene hat einen eigenen Lebenszyklus, der vom Browsertab unabhängig ist. Eine einfache Seitenaktualisierung reicht nicht aus, um einen Service Worker zu aktualisieren – genauso, wie Sie es nicht erwarten würden, wenn eine Seite aktualisiert wird, um Code zu aktualisieren, der auf einem Server bereitgestellt ist. Jede Ebene hat ihre eigenen Regeln für die Aktualisierung.

Im Spiel Service Workies behandeln wir die vielen Details des Service Worker-Lebenszyklus und bieten Ihnen eine Menge Übung.

Leistungsstark, aber begrenzt

Ein Service Worker auf Ihrer Website hat unglaubliche Vorteile. Ihre Website kann:

  • reibungslos zu funktionieren, auch wenn der Nutzer offline ist.
  • Leistungssteigerungen durch Caching
  • Push-Benachrichtigungen verwenden
  • Sie müssen als PWA installiert sein.

So viel können Service-Worker tun, sind sie durch ihr Design eingeschränkt. Sie können nichts synchron oder im selben Thread wie deine Website tun. Das bedeutet, Sie haben keinen Zugriff auf:

  • localStorage
  • DOM
  • das Fenster

Die gute Nachricht ist, dass es verschiedene Möglichkeiten gibt, wie deine Seite mit dem Service Worker kommunizieren kann, darunter direkte postMessage, 1:1-Nachrichtenkanäle und 1:n-Broadcast-Kanäle.

Langlebig, aber kurzlebig

Ein aktiver Service Worker lebt auch dann weiter, wenn ein Nutzer Ihre Website verlässt oder den Tab schließt. Der Browser behält diesen Service Worker bei, damit er verfügbar ist, wenn der Nutzer das nächste Mal zu Ihrer Website zurückkehrt. Bevor die erste Anfrage gestellt wird, hat der Service Worker die Möglichkeit, diese abzufangen und die Kontrolle über die Seite zu übernehmen. Dies ermöglicht es einer Website, offline zu arbeiten. Der Service Worker kann eine im Cache gespeicherte Version der Seite selbst dann bereitstellen, wenn der Nutzer keine Verbindung zum Internet hat.

In Service Workies wird dieses Konzept veranschaulicht, in dem der freundlicher Service Worker Kolohe Anfragen abfängt und verarbeitet.

Beendet

Obwohl Servicemitarbeiter unsterblich zu sein scheinen, können sie so gut wie jederzeit eingestellt werden. Der Browser möchte keine Ressourcen für einen Service Worker verschwenden, der gerade nichts tut. Wenn ein Dienst beendet wird, bedeutet das nicht, dass er geschlossen wird – der Service Worker bleibt installiert und aktiviert. Sie wurde einfach in den Ruhemodus versetzt. Wenn sie das nächste Mal benötigt wird (z.B. zur Verarbeitung einer Anfrage), wird sie vom Browser wieder aktiviert.

waitUntil

Da ständig die Möglichkeit besteht, in den Ruhemodus versetzt zu werden, benötigt Ihr Service Worker eine Möglichkeit, den Browser darüber zu informieren, wenn er etwas Wichtiges tut und nicht das Gefühl hat, ein Nickerchen zu machen. Hier kommt event.waitUntil() ins Spiel. Diese Methode verlängert den Lebenszyklus, in dem sie verwendet wird, und verhindert so, dass sie gestoppt wird und in die nächste Phase ihres Lebenszyklus übergeht, bis wir bereit sind. Dadurch haben wir Zeit, Caches einzurichten, Ressourcen aus dem Netzwerk abzurufen usw.

In diesem Beispiel wird dem Browser mitgeteilt, dass die Installation des Service Workers erst abgeschlossen ist, wenn der Cache assets erstellt und das Bild eines Schwerts eingefügt wurde:

self.addEventListener("install", event => {
  event.waitUntil(
    caches.open("assets").then(cache => {
      return cache.addAll(["/weapons/sword/blade.png"]);
    })
  );
});

Auf globaler Ebene achten

In diesem Fall wird der globale Geltungsbereich des Service Workers zurückgesetzt. Achten Sie also darauf, keinen globalen Status in Ihrem Service Worker zu verwenden, da Sie sonst traurig sind, wenn er das nächste Mal wieder aufwacht und einen anderen Status als erwartet aufweist.

Betrachten Sie das folgende Beispiel mit einem globalen Status:

const favoriteNumber = Math.random();
let hasHandledARequest = false;

self.addEventListener("fetch", event => {
  console.log(favoriteNumber);
  console.log(hasHandledARequest);
  hasHandledARequest = true;
});

Dieser Service Worker protokolliert bei jeder Anfrage eine Nummer, z. B. 0.13981866382421893. Die Variable hasHandledARequest ändert sich ebenfalls in true. Da der Service Worker nun für eine Weile inaktiv ist, wird er vom Browser angehalten. Bei der nächsten Anfrage wird der Service Worker wieder benötigt, sodass der Browser ihn aktiviert. Das Skript wird noch einmal ausgewertet. Jetzt wird hasHandledARequest auf false zurückgesetzt und favoriteNumber ist etwas ganz anderes: 0.5907281835659033.

Sie können sich nicht auf den in einem Service Worker gespeicherten Status verlassen. Das Erstellen von Instanzen von Dingen wie Nachrichtenkanälen kann außerdem Fehler verursachen: Sie erhalten jedes Mal, wenn der Service Worker beendet/startet, eine brandneue Instanz.

In Kapitel 3 der Service Workies sehen wir, dass unser angehaltener Service Worker während der Wartezeit bis zum Erwachen alle Farben verliert.

Visualisierung eines angehaltenen Service Workers

Zusammen, aber getrennt

Ihre Seite kann jeweils nur von einem Service Worker gesteuert werden. Es können aber zwei Service Worker gleichzeitig installiert sein. Wenn Sie den Service Worker-Code ändern und die Seite aktualisieren, bearbeiten Sie den Service Worker gar nicht. Service Worker sind unveränderlich. Sie sind stattdessen dabei, einen ganz neuen zu erstellen. Dieser neue Service Worker (wir nennen ihn SW2) wird installiert, aber noch nicht aktiviert. Sie muss warten, bis der aktuelle Service Worker (SW1) beendet wird (wenn der Nutzer die Website verlässt).

Kommunikation mit den Caches eines anderen Service Workers

Während der Installation kann SW2 die Einrichtung vorbereiten – normalerweise das Erstellen und Auffüllen von Caches. Aber Achtung: Der neue Service Worker hat Zugriff auf alle Inhalte, auf die er Zugriff hat. Wenn Sie nicht vorsichtig sind, kann Ihr neuer Service Worker Ihren aktuellen Service Worker durcheinanderbringen. Einige Beispiele, die Probleme verursachen können:

  • SW2 könnte einen Cache löschen, der von SW1 aktiv verwendet wird.
  • SW2 könnte den Inhalt eines von SW1 verwendeten Cache bearbeiten, was dazu führt, dass SW1 mit Inhalten antwortet, die von der Seite nicht erwartet werden.

Überspringen überspringenWarten

Ein Service Worker kann auch die riskante skipWaiting()-Methode verwenden, um die Kontrolle über die Seite zu übernehmen, sobald die Installation abgeschlossen ist. Dies ist im Allgemeinen keine gute Idee, es sei denn, Sie möchten absichtlich einen fehlerhaften Servicemitarbeiter ersetzen. Der neue Service Worker verwendet möglicherweise aktualisierte Ressourcen, die von der aktuellen Seite nicht erwartet werden, was zu Fehlern und Programmfehlern führt.

Bereinigung starten

Wenn Sie verhindern möchten, dass Ihre Service Worker sich gegenseitig überschreiben, können Sie dafür sorgen, dass sie unterschiedliche Caches verwenden. Dies erreichen Sie am einfachsten, 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, verschieben Sie das version so, dass es mit einem völlig anderen Cache als mit dem vorherigen Service Worker das tut, was es benötigt.

Visualisierung eines Cache

Reinigung beenden

Sobald Ihr Service Worker den Status activated erreicht, wissen Sie, dass er übernommen hat und der vorherige Service Worker redundant ist. An dieser Stelle ist es wichtig, nach dem alten Service Worker eine Bereinigung durchzuführen. Dabei werden nicht nur die Speicherlimits Ihrer Nutzer berücksichtigt, sondern auch unbeabsichtigte Fehler vermieden.

Die Methode caches.match() ist eine häufig verwendete Tastenkombination zum Abrufen eines Elements aus allen Caches, in denen eine Übereinstimmung vorliegt. Die Caches werden jedoch in der Reihenfolge durchlaufen, in der sie erstellt wurden. Angenommen, Sie haben zwei Versionen der Skriptdatei app.js in zwei verschiedenen Caches: assets-1 und assets-2. Für deine Seite wird das neuere Skript erwartet, das in assets-2 gespeichert ist. Wenn du den alten Cache jedoch nicht gelöscht hast, gibt caches.match('app.js') den alten Cache von assets-1 zurück und funktioniert höchstwahrscheinlich nicht.

Nach vorherigen Service Workern müssen Sie lediglich 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 ist zwar ein bisschen Arbeit und Disziplin, zu verhindern, dass sich Service Worker gegenseitig überhäufen, aber den Aufwand lohnt sich.

Service Worker-Mentalität

Wenn Sie die richtige Denkweise bei den Service Workern einplanen, können Sie Ihre eigene mit Selbstvertrauen aufbauen. Sobald Sie sich mit ihnen vertraut gemacht haben, können Sie fantastische Erlebnisse für Ihre Nutzer schaffen.

Wenn Sie all dies verstehen möchten, indem Sie ein Spiel spielen, dann haben Sie Glück! Spiele Service Workies und erfahre, wie ein Service Worker die Offline-Tierwesen besiegt.