Sofortige Navigation

Ergänzung herkömmlicher Prefetch-Techniken durch Service Worker

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

Das Ausführen einer Aufgabe auf einer Website umfasst in der Regel mehrere Schritte. Zum Beispiel kann der Kauf eines Produkts auf einer E-Commerce-Website nach einem Produkt suchen, einen Artikel aus der Ergebnisliste auswählen, den Artikel in den Warenkorb legen und den Vorgang durch Bezahlen abschließen.

Technisch gesehen bedeutet das Wechseln zwischen verschiedenen Seiten eine Navigationsanfrage. Im Allgemeinen sollten Sie keine langlebigen Cache-Control-Header verwenden, um die HTML-Antwort auf eine Navigationsanfrage im Cache zu speichern. Sie sollten normalerweise über das Netzwerk mit Cache-Control: no-cache erfüllt werden, um sicherzustellen, dass der HTML-Code sowie die Kette nachfolgender Netzwerkanfragen (angemessene) aktuell ist. Wenn Nutzer jedes Mal, wenn sie eine neue Seite aufrufen, eine Verbindung zum Netzwerk herstellen müssen, ist die Navigation möglicherweise langsam. Zumindest bedeutet das, dass sie nicht zuverlässig schnell ist.

Um diese Anfragen zu beschleunigen und die Aktion des Nutzers vorherzusehen, können Sie diese Seiten und Assets vorab anfordern und für kurze Zeit im Cache speichern, bis der Nutzer auf die Links klickt. Diese Methode wird als Vorabruf bezeichnet und in der Regel implementiert, indem <link rel="prefetch">-Tags zu Seiten hinzugefügt werden, die die Ressource angeben, die dem Prefetch gesendet werden soll.

In diesem Leitfaden erfahren Sie, wie Service Worker zusätzlich zu herkömmlichen Prefetch-Techniken 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 dynamisch in einige Teile des Ablaufs eingefügt. Auf Listenseiten wird beispielsweise die nächste Ergebnisseite abgerufen, sobald der Nutzer ans Ende der Liste scrollt:

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

Vorabgerufene Dateien werden mit der Priorität „Niedrigste“ angefordert und je nach Browser im HTTP-Cache oder im Arbeitsspeichercache gespeichert, je nachdem, ob die Ressource im Cache gespeichert werden kann oder nicht. Ab Chrome 85 beträgt dieser Wert beispielsweise 5 Minuten. Ressourcen werden fünf Minuten lang aufbewahrt. Danach gelten die normalen Cache-Control-Regeln für die Ressource.

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

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

Virgilio Sport-Logo

Dank dreiwöchiger Beobachtungen konnte Virgilio Sport feststellen, dass die Ladezeiten für die Navigation zu Artikeln um 78% verbessert wurden und die Anzahl der Artikelimpressionen um 45% gestiegen ist.

Screenshot der Startseite und Artikelseiten von Virgilio Sport mit Messwerten zu den Auswirkungen nach dem Prefetch

Precaching mit Workbox implementieren

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

1. Statische Seiten und Seitenunterressourcen vorab im Cache speichern

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

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

Precaching statischer Seiten

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

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

Untergeordnete Ressourcen von Seiten werden vorab im Cache gespeichert

Das Pre-Caching statischer Assets, die von verschiedenen Bereichen der Website verwendet werden könnten (z.B. JavaScript, CSS usw.), ist eine allgemeine Best Practice und kann in Prefetch-Szenarien zusätzlichen Schub geben.

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 Produktseite 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 die Seitenunterressourcen zur Precache-Liste im Service Worker 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 Vorteil bieten, dass diese Ressourcen für die Offlinenutzung verfügbar sind.

Im vorherigen Beispiel könnte man die <link rel="prefetch">, die zum Vorabruf einer Produktseite verwendet wird, durch eine Workbox-Laufzeit-Caching-Strategie ergänzen.

Gehen Sie dazu so vor:

  • Füge der Seite ein <link rel="prefetch">-Tag hinzu:
 <link rel="prefetch" href="/phones/smartphone-5x.html" as="document">
  • Implementieren Sie für diese Anfragetypen eine Strategie zur Laufzeit-Caching im Service Worker:
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 Strategie des Typs Veralteter Zeitraum für erneute Validierung entschieden. Bei dieser Strategie können Seiten gleichzeitig sowohl aus dem Cache als auch aus dem Netzwerk angefordert werden. Die Antwort stammt aus dem Cache, sofern verfügbar, andernfalls aus dem Netzwerk. Der Cache wird bei jeder erfolgreichen Anfrage mit der Netzwerkantwort aktualisiert.

3. Vorabrufen 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 den Vorabruf so effizient wie möglich gestalten soll.

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 Seite mit Produkteinträgen vorab abzurufen, muss man möglicherweise basierend auf einer API-Antwort mehrere <link rel="prefetch">-Tags dynamisch in die Seite einfügen. Dies kann für einen Moment Zeit im Hauptthread der Seite verbrauchen und die Implementierung erschweren.

Verwenden Sie in solchen Fällen eine „Page-to-Service-Worker-Kommunikationsstrategie“, um die Aufgabe des Vorabrufs vollständig an den Service Worker zu delegieren. Diese Art der Kommunikation kann mit worker.postMessage() erreicht werden:

Ein Symbol einer Seite, über die eine Zwei-Wege-Kommunikation mit einem Service Worker ermöglicht wird.

Das Workbox-Fenster-Paket vereinfacht diese Art der Kommunikation und abstrahiert viele Details des zugrunde liegenden Aufrufs.

Der Vorabruf mit einem Workbox-Fenster kann so 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 Vorabruf:
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 Prefetch-URL eine fetch()-Anfrage zu senden:
addEventListener('message', (event) => {
  if (event.data.type === 'PREFETCH_URLS') {
    // Fetch URLs and store them in the cache
  }
});