Sofortige Navigation

Ergänzung herkömmlicher Vorabruftechniken durch Service Worker.

Demián Renzulli
Demián Renzulli
Gilberto Cocchi
Gilberto Cocchi

Das Durchführen einer Aufgabe auf einer Website umfasst in der Regel mehrere Schritte. Zum Beispiel könnte der Kauf eines Produkts auf einer E-Commerce-Website die Suche nach einem Produkt, das Auswählen eines Artikels aus der Ergebnisliste, das Hinzufügen des Artikels zum Warenkorb und das Abschließen des Vorgangs an der Kasse beinhalten.

Technisch gesehen bedeutet das Wechseln zwischen verschiedenen Seiten eine Navigationsanfrage. Grundsätzlich solltest du keine langlebigen Cache-Control-Header verwenden, um die HTML-Antwort für eine Navigationsanfrage im Cache zu speichern. Sie sollten normalerweise über das Netzwerk mit Cache-Control: no-cache zufrieden sein, um sicherzustellen, dass der HTML-Code zusammen mit der Kette nachfolgender Netzwerkanfragen (in angemessenem Maße) aktuell ist. Wenn der Nutzer jedes Mal das Netzwerk aufrufen muss, wenn er eine neue Seite aufruft, kann das leider dazu führen, dass jede Navigation langsam ist – zumindest nicht zuverlässig.

Wenn Sie die Aktionen des Nutzers vorhersehen können, können Sie diese Seiten und Assets im Voraus anfordern und sie für kurze Zeit im Cache speichern, bis der Nutzer auf diese Links klickt. Diese Technik wird als Vorabruf bezeichnet und wird häufig implementiert, indem Seiten <link rel="prefetch">-Tags hinzugefügt werden, um die Ressource für den Vorabruf anzugeben.

In diesem Leitfaden werden verschiedene Möglichkeiten vorgestellt, wie Service Worker als Ergänzung der traditionellen Vorabruftechniken verwendet werden können.

Produktionsfälle

MercadoLibre ist die größte E-Commerce-Website Lateinamerikas. Um die Navigation zu beschleunigen, werden <link rel="prefetch">-Tags in einigen Teilen des Ablaufs dynamisch eingefügt. Auf Eintragsseiten wird beispielsweise die nächste Ergebnisseite abgerufen, sobald der Nutzer ans Ende des Eintrags scrollt:

Screenshot der Seiten eins und zwei von MercadoLibre und eines Link-Prefetch-Tags, das beide verbindet.

Vorabgerufene Dateien werden mit der niedrigsten Priorität angefordert und im HTTP-Cache oder im Speichercache (je nachdem, ob die Ressource im Cache speicherbar ist oder nicht) für einen Zeitraum gespeichert werden, der je nach Browser variiert. Ab Chrome 85 beträgt dieser Wert beispielsweise 5 Minuten. Ressourcen werden fünf Minuten lang aufbewahrt. Anschließend gelten die normalen Cache-Control-Regeln für die Ressource.

Mit dem Service Worker-Caching können Sie die Lebensdauer von Prefetch-Ressourcen über das Zeitfenster von fünf Minuten hinaus verlängern.

Das italienische Sportportal Virgilio Sport nutzt beispielsweise Service Worker, um die beliebtesten Beiträge auf seiner Startseite vorab abzurufen. Außerdem wird die Network Information API verwendet, um den Vorabruf von Nutzern mit einer 2G-Verbindung zu vermeiden.

Virgilio Sport-Logo

Daher hat Virgilio Sport über drei Wochen hinweg beobachtete, dass die Ladezeiten für die Navigation zu Artikeln um 78% stiegen und die Anzahl der Artikelimpressionen um 45% stieg.

Screenshot der Startseite und Artikelseiten von Virgilio Sport mit Messwerten zur Auswirkung nach dem Prefetch

Precaching mit Workbox implementieren

Im folgenden Abschnitt verwenden wir Workbox, um zu zeigen, wie verschiedene Caching-Verfahren im Service Worker implementiert werden. Diese können als Ergänzung zu <link rel="prefetch"> oder sogar als Ersatz verwendet werden, indem diese Aufgabe vollständig an den Service Worker delegiert wird.

1. Statische Seiten und Unterressourcen für Seiten vorab im Cache speichern

Das Precaching ist die Fähigkeit des Service Workers, Dateien während der Installation im Cache zu speichern.

In den folgenden Fällen wird mit dem Precach ein ähnliches Ziel erreicht wie durch den Prefetch: die Navigation beschleunigen.

Statische Seiten vorab im Cache speichern

Bei Seiten, die zum Zeitpunkt der Erstellung (z.B. about.html, contact.html) oder auf vollständig statischen Websites generiert werden, können die Dokumente der Website einfach der Precache-Liste hinzugefügt werden, sodass sie bei jedem Zugriff des Nutzers bereits im Cache verfügbar sind:

workbox.precaching.precacheAndRoute([
  {url: '/about.html', revision: 'abcd1234'},
  // ... other entries ...
]);

Unterressourcen für Seiten vorab im Cache speichern

Das Pre-Caching statischer Assets, die von den verschiedenen Bereichen der Website verwendet werden können (z.B. JavaScript, CSS usw.), ist eine allgemeine Best Practice und kann bei Prefetch-Szenarien einen zusätzlichen Auftrieb bieten.

Um die Navigation auf einer E-Commerce-Website zu beschleunigen, können Sie <link rel="prefetch">-Tags auf Angebotsseiten verwenden, um Produktdetailseiten für die ersten Produkte einer Eintragsseite vorab abzurufen. Wenn Sie die Unterressourcen der Produktseiten bereits vorab im Cache gespeichert haben, kann dies die Navigation noch beschleunigen.

So implementieren Sie dies:

  • Füge der Seite ein <link rel="prefetch">-Tag hinzu:
 <link rel="prefetch" href="/phones/smartphone-5x.html" as="document">
  • Fügen Sie der Precache-Liste im Service Worker die Unterressourcen der Seite hinzu:
workbox.precaching.precacheAndRoute([
  '/styles/product-page.ac29.css',
  // ... other entries ...
]);

2. Lebensdauer von Prefetch-Ressourcen verlängern

Wie bereits erwähnt, ruft <link rel="prefetch"> Ressourcen ab und speichert sie für einen begrenzten Zeitraum im HTTP-Cache. Danach gelten die Cache-Control-Regeln für eine Ressource. Ab Chrome 85 beträgt dieser Wert 5 Minuten.

Mit Service Workern können Sie die Lebensdauer der Prefetch-Seiten verlängern und gleichzeitig den zusätzlichen Vorteil bieten, dass Sie diese Ressourcen für die Offlinenutzung verfügbar machen.

Im vorherigen Beispiel könnte das <link rel="prefetch">, das zum Vorabruf einer Produktseite verwendet wurde, durch eine Workbox-Laufzeit-Caching-Strategie ergänzt werden.

So implementieren Sie dies:

  • Füge der Seite ein <link rel="prefetch">-Tag hinzu:
 <link rel="prefetch" href="/phones/smartphone-5x.html" as="document">
  • Implementieren Sie im Service Worker eine Strategie für das Laufzeit-Caching für diese Anfragetypen:
new workbox.strategies.StaleWhileRevalidate({
  cacheName: 'document-cache',
  plugins: [
    new workbox.expiration.Plugin({
      maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
    }),
  ],
});

In diesem Fall haben wir uns für eine veraltete Strategie zur erneuten Überprüfung während der Überprüfung entschieden. Bei dieser Strategie können Seiten parallel vom Cache und vom Netzwerk angefordert werden. Die Antwort kommt aus dem Cache, falls verfügbar, andernfalls aus dem Netzwerk. Der Cache wird bei jeder erfolgreichen Anfrage immer mit der Netzwerkantwort aktualisiert.

3. Vorabruf an Service Worker delegieren

In den meisten Fällen ist die Verwendung von <link rel="prefetch"> der beste Ansatz. Das Tag ist ein Ressourcenhinweis, der den Vorabruf so effizient wie möglich macht.

In einigen Fällen kann es jedoch besser sein, diese Aufgabe vollständig an den Service Worker zu delegieren. Wenn Sie beispielsweise die ersten Produkte auf einer clientseitig gerenderten Seite mit Produktinformationen vorab abrufen möchten, müssen Sie möglicherweise mehrere <link rel="prefetch">-Tags basierend auf einer API-Antwort dynamisch in die Seite einfügen. Dies kann im Hauptthread der Seite kurzzeitig Zeit in Anspruch nehmen und die Implementierung erschweren.

Verwenden Sie in solchen Fällen eine „Kommunikationsstrategie von Seite zu Service Worker“, um den Vorabruf vollständig an den Service Worker zu delegieren. Diese Art der Kommunikation kann mithilfe von worker.postMessage() erreicht werden:

Ein Symbol einer Seite, die eine Zwei-Wege-Kommunikation mit einem Service Worker herstellt.

Das Workbox Window-Paket vereinfacht diese Art der Kommunikation, da es viele Details des zugrunde liegenden Aufrufs abstrahiert.

Der Vorabruf mit Workbox-Fenster kann folgendermaßen implementiert werden:

  • Auf der Seite: Rufen Sie den Service Worker auf und übergeben Sie ihm den Nachrichtentyp und die Liste der URLs für den Prefetch:
const wb = new Workbox('/sw.js');
wb.register();

const prefetchResponse = await wb.messageSW({type: 'PREFETCH_URLS', urls: […]});
  • Im Service Worker implementieren Sie einen Nachrichten-Handler, um für jede URL, die vorab abgerufen werden soll, eine fetch()-Anfrage auszugeben:
addEventListener('message', (event) => {
  if (event.data.type === 'PREFETCH_URLS') {
    // Fetch URLs and store them in the cache
  }
});