Sofortige Navigation

Traditionelle prefetching-Techniken durch Service Worker ergänzen.

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

Die Ausführung einer Aufgabe auf einer Website umfasst in der Regel mehrere Schritte. Der Kauf eines Produkts auf einer E-Commerce-Website kann beispielsweise die Suche nach einem Produkt, die Auswahl eines Artikels aus der Liste der Ergebnisse, das Hinzufügen des Artikels zum Einkaufswagen und den Abschluss des Vorgangs durch den Bezahlvorgang umfassen.

Technisch gesehen bedeutet das Wechseln zwischen verschiedenen Seiten, eine Navigationsanfrage zu stellen. Als Faustregel gilt: Verwenden Sie keine langlebigen Cache-Control-Header, um die HTML-Antwort für eine Navigationsanfrage im Cache zu speichern. Sie sollten normalerweise über das Netzwerk mit Cache-Control: no-cache erfüllt werden, damit die HTML-Datei und die nachfolgenden Netzwerkanfragen (relativ) aktuell sind. Wenn jedes Mal, wenn der Nutzer zu einer neuen Seite wechselt, das Netzwerk kontaktiert werden muss, kann die Navigation leider langsam sein. Zumindest ist sie nicht zuverlässig schnell.

Wenn Sie die Aktion des Nutzers vorhersehen können, können Sie diese Seiten und Assets vorab anfordern und für kurze Zeit im Cache speichern, bis der Nutzer auf diese Links klickt. Diese Methode wird als Prefetching bezeichnet. Sie wird in der Regel durch Einfügen von <link rel="prefetch">-Tags auf Seiten implementiert, die die zu vorab ladende Ressource angeben.

In diesem Leitfaden erfahren Sie, wie Dienstprogramme als Ergänzung zu herkömmlichen Prefetching-Techniken verwendet werden können.

Produktionsfälle

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

Screenshot der Eintragsseiten 1 und 2 von MercadoLibre und ein Link Prefetch-Tag, das beide verbindet.

Vorab abgerufene Dateien werden mit der Priorität „Niedrigste“ angefordert und je nachdem, ob die Ressource im Cache gespeichert werden kann oder nicht, für eine Zeitspanne, die je nach Browser variiert, im HTTP-Cache oder im Speichercache gespeichert. In Chrome 85 beträgt dieser Wert beispielsweise 5 Minuten. Ressourcen werden fünf Minuten lang beibehalten, danach gelten die normalen Cache-Control-Regeln für die Ressource.

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

Das italienische Sportportal Virgilio Sport verwendet beispielsweise Dienst-Worker, um die beliebtesten Beiträge auf der Startseite vorab zu laden. Außerdem wird die Network Information API verwendet, um das Vorabladen für Nutzer mit einer 2G-Verbindung zu vermeiden.

Logo von Virgilio Sport

In den drei Wochen, in denen die Änderungen beobachtet wurden, konnte Virgilio Sport eine 78%ige Verbesserung bei den Ladezeiten für die Navigation zu Artikeln und eine 45%ige Steigerung der Anzahl der Artikelimpressionen feststellen.

Screenshot der Start- und Artikelseiten von Virgilio Sport mit Leistungsmesswerten nach dem Vorabladen

Precaching mit Workbox implementieren

Im folgenden Abschnitt zeigen wir anhand von Workbox, wie Sie verschiedene Caching-Techniken im Service Worker implementieren, die als Ergänzung zu <link rel="prefetch"> oder sogar als Ersatz dafür verwendet werden können, indem Sie diese Aufgabe vollständig an den Service Worker delegieren.

1. Statische Seiten und Seitenunterressourcen vorab im Cache speichern

Beim Vorab-Caching kann der Dienst-Worker Dateien während der Installation im Cache speichern.

In den folgenden Fällen wird das Precaching verwendet, um ein ähnliches Ziel wie das Prefetching zu erreichen: die Navigation zu beschleunigen.

Statische Seiten vorab im Cache speichern

Bei Seiten, die zur Buildzeit generiert werden (z.B. about.html, contact.html), oder bei vollständig statischen Websites können Sie die Dokumente der Website einfach der Pre-Cache-Liste hinzufügen, damit sie bei jedem Zugriff des Nutzers bereits im Cache verfügbar sind:

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

Untergeordnete Ressourcen der Seite vorab laden

Das Precaching statischer Assets, die in den verschiedenen Bereichen der Website verwendet werden können (z. B. JavaScript, CSS), ist eine allgemeine Best Practice und kann bei Prefetching-Szenarien einen zusätzlichen Schub geben.

Um die Navigation auf einer E-Commerce-Website zu beschleunigen, können Sie <link rel="prefetch">-Tags auf Eintragsseiten verwenden, um Produktdetailseiten für die ersten Produkte einer Eintragsseite vorab zu laden. Wenn Sie die untergeordneten Ressourcen der Produktseite bereits im Cache gespeichert haben, kann die Navigation noch schneller erfolgen.

So implementieren Sie diese Funktion:

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

2. Lebensdauer von Ressourcen für die Vorab-Datenübertragung verlängern

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

Mit Service Workern können Sie die Lebensdauer der vorab geladenen Seiten verlängern und diese Ressourcen zusätzlich für die Offlinenutzung verfügbar machen.

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

So implementieren Sie das:

  • Fügen Sie der Seite ein <link rel="prefetch">-Tag hinzu:
 <link rel="prefetch" href="/phones/smartphone-5x.html" as="document">
  • Implementieren Sie im Service Worker eine Laufzeit-Caching-Strategie für diese Arten von Anfragen:
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 Stale-While-Revalidate-Strategie entschieden. Bei dieser Strategie können Seiten parallel aus dem Cache und dem Netzwerk angefordert werden. Die Antwort stammt aus dem Cache, sofern verfügbar, andernfalls aus dem Netzwerk. Der Cache wird bei jeder erfolgreichen Anfrage immer mit der Netzwerkantwort aktualisiert.

3. Prefetching an den Service Worker delegieren

In den meisten Fällen ist es am besten, <link rel="prefetch"> zu verwenden. Das Tag ist ein Ressourcenhinweis, der das Vorabladen so effizient wie möglich macht.

In einigen Fällen ist es jedoch besser, diese Aufgabe vollständig an den Service Worker zu delegieren. Beispiel: Um die ersten Produkte auf einer clientseitig gerenderten Produktlistenseite vorab zu laden, müssen möglicherweise mehrere <link rel="prefetch">-Tags basierend auf einer API-Antwort dynamisch in die Seite eingefügt werden. Dies kann den Hauptthread der Seite vorübergehend belasten und die Implementierung erschweren.

Verwenden Sie in solchen Fällen eine „Kommunikationsstrategie für Seiten und Dienst-Worker“, um die Aufgabe des Vorabladens vollständig an den Dienst-Worker zu delegieren. Diese Art der Kommunikation kann mit worker.postMessage() erreicht werden:

Ein Symbol für eine Seite, die eine bidirektionale Kommunikation mit einem Dienstarbeiter ermöglicht.

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

Das Vorabladen mit dem Workbox-Fenster kann so implementiert werden:

  • Auf der Seite: Rufen Sie den Service Worker auf und übergeben Sie ihm die Art der Nachricht und die Liste der URLs, die vorab abgerufen werden sollen:
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 zu senden:
addEventListener('message', (event) => {
 
if (event.data.type === 'PREFETCH_URLS') {
   
// Fetch URLs and store them in the cache
 
}
});