Funkcje natychmiastowej nawigacji

Uzupełnianie tradycyjnych technik pobierania z wyprzedzeniem mechanizmami Service Worker.

Wykonywanie zadania w witrynie zwykle obejmuje kilka kroków. Na przykład zakup produktu w witrynie e-commerce może obejmować wyszukanie produktu, wybranie go z listy wyników, dodanie go do koszyka i ukończenie operacji poprzez transakcję.

Technicznie rzecz biorąc, poruszanie się po różnych stronach oznacza przesłanie żądania nawigacji. Zasadniczo nie należy używać długotrwałych nagłówków Cache-Control do buforowania odpowiedzi HTML na żądanie nawigacji. Zazwyczaj żądania są realizowane przy użyciu sieci (Cache-Control: no-cache), aby zapewnić, że kod HTML i łańcuch kolejnych żądań sieciowych są (rozsądnie) aktualne. Konieczność łączenia się z siecią przy każdym przejściu na nową stronę niestety oznacza, że każda nawigacja może przebiegać wolno – przynajmniej nie da się jej prawidłowo szybko.

Jeśli możesz przewidzieć działania użytkownika, możesz to przyspieszyć i zachować te strony i komponenty w pamięci podręcznej przez krótki czas, aż użytkownik kliknie te linki. Ta metoda nazywa się pobieraniem z wyprzedzeniem i zwykle jest wdrażana przez dodanie do stron tagów <link rel="prefetch">, które wskażą zasób do pobrania z wyprzedzeniem.

W tym przewodniku omówimy różne sposoby stosowania skryptów service worker jako uzupełnienie tradycyjnych technik pobierania z wyprzedzeniem.

Sprawy produkcyjne

MercadoLibre to największa witryna e-commerce w Ameryce Łacińskiej. Aby przyspieszyć nawigację, w niektórych częściach procesu dynamicznie wstawiają tagi <link rel="prefetch">. Na przykład w przypadku stron z informacjami o aplikacji pobierają one następną stronę wyników, gdy tylko użytkownik przewinie stronę w dół:

Zrzut ekranu przedstawiający stronę nr 1 i 2 w MercadoLibre oraz tag pobierania z wyprzedzeniem, który łączy obie strony.

Wstępnie wczytywane pliki są żądane z najniższym priorytetem i są przechowywane w pamięci podręcznej HTTP lub pamięci podręcznej (w zależności od tego, czy zasób można zapisać w pamięci podręcznej) przez określony czas, który różni się w zależności od przeglądarki. Na przykład w Chrome 85 ta wartość wynosi 5 minut. Zasoby są przechowywane przez 5 minut. Po tym czasie obowiązują normalne reguły Cache-Control dotyczące zasobu.

Użycie pamięci podręcznej skryptu service worker może pomóc wydłużyć czas pobierania zasobów z wyprzedzeniem poza 5-minutowy okres.

Na przykład włoski portal sportowy Virgilio Sport wykorzystuje pracowników usług do wstępnego pobierania najpopularniejszych postów na stronie głównej. Korzystają one też z interfejsu Network Information API, aby uniknąć wstępnego pobierania danych w przypadku użytkowników korzystających z połączenia 2G.

Logo Virgilio Sport.

W efekcie w ciągu ponad 3 tygodni firma Virgilio Sport zaobserwowała czas wczytywania artykułów, który poprzedza o 78%, a liczba wyświetleń artykułów zwiększyła się o 45%.

Zrzut ekranu strony głównej i artykułów na stronie Virgilio Sport z danymi dotyczącymi wpływu po pobraniu z wyprzedzeniem.

Wdrażanie wstępnego buforowania w Workbox

W następnej sekcji użyjemy Workbox, aby pokazać, jak wdrożyć w skrypcie service worker różne techniki buforowania, których można użyć jako uzupełnienie metody <link rel="prefetch"> lub nawet jej zastąpienie, przez pełne przekazanie tego zadania skryptowi service worker.

1. Wstępne buforowanie stron statycznych i podzasobów stron

Wstępne buforowanie to możliwość przez skrypt service worker zapisywania plików w pamięci podręcznej podczas instalacji.

W tych przypadkach wstępne wczytywanie jest używane do osiągnięcia celu podobnego do pobierania z wyprzedzeniem – przyspieszenia nawigacji.

Wstępnie buforowane strony statyczne

W przypadku stron wygenerowanych w czasie kompilacji (np. about.html, contact.html) lub w całkowicie statycznych witrynach można po prostu dodać dokumenty z tej witryny do listy wstępnego wczytywania, dzięki czemu będą one dostępne w pamięci podręcznej za każdym razem, gdy użytkownik je otworzy:

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

Podrzędne zasoby strony wstępnego buforowania

Wstępne zapisywanie zasobów statycznych w pamięci podręcznej, z których mogą korzystać różne sekcje witryny (np. JavaScript, CSS itp.), jest ogólną sprawdzoną metodą i może zapewnić dodatkową skuteczność w sytuacjach pobierania z wyprzedzeniem.

Aby przyspieszyć nawigację w witrynie e-commerce, możesz użyć tagów <link rel="prefetch"> na stronach z listami, aby wstępnie pobrać strony ze szczegółami kilku pierwszych produktów na stronie z informacjami o produkcie. Jeśli masz już w pamięci podręcznej wstępnie zasoby podrzędne strony produktu, może to jeszcze bardziej przyspieszyć nawigację.

Aby to zrobić:

  • Dodaj do strony tag <link rel="prefetch">:
 <link rel="prefetch" href="/phones/smartphone-5x.html" as="document">
  • Dodaj zasoby podrzędne strony do listy pamięci podręcznej wstępnego w skrypcie service worker:
workbox.precaching.precacheAndRoute([
  '/styles/product-page.ac29.css',
  // ... other entries ...
]);

2. Wydłuż czas przechowywania zasobów pobierania z wyprzedzeniem

Jak już wspomnieliśmy, <link rel="prefetch"> pobiera i przechowuje zasoby w pamięci podręcznej HTTP przez ograniczony czas. Po upływie tego czasu obowiązują reguły Cache-Control dotyczące zasobu. Od wersji Chrome 85 ta wartość wynosi 5 minut.

Skrypty service worker pozwalają wydłużyć czas życia stron pobierania z wyprzedzeniem, a jednocześnie zapewniają dodatkową korzyść w postaci udostępnienia tych zasobów w trybie offline.

W poprzednim przykładzie można uzupełnić pole <link rel="prefetch"> używane do wstępnego pobierania strony produktu strategią buforowania środowiska wykonawczego Workspace.

Aby to zrobić:

  • Dodaj do strony tag <link rel="prefetch">:
 <link rel="prefetch" href="/phones/smartphone-5x.html" as="document">
  • W skrypcie service worker zaimplementuj strategię buforowania czasu działania w przypadku tych typów żądań:
new workbox.strategies.StaleWhileRevalidate({
  cacheName: 'document-cache',
  plugins: [
    new workbox.expiration.Plugin({
      maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
    }),
  ],
});

W tym przypadku zdecydowaliśmy się zastosować strategię, która nie działa w czasie ponownej weryfikacji. W ramach tej strategii żądania stron mogą być wysyłane równolegle z pamięci podręcznej i z sieci. Odpowiedź pochodzi z pamięci podręcznej, jeśli jest dostępna. W przeciwnym razie z sieci. Pamięć podręczna jest zawsze aktualizowana w odpowiedzi na żądanie sieci przy każdym udanym żądaniu.

3. Przekaż wstępne pobieranie do skryptu service worker

W większości przypadków najlepiej jest użyć atrybutu <link rel="prefetch">. Tag to wskazówka dotycząca zasobów, która ma na celu jak najszybsze pobieranie danych z wyprzedzeniem.

W niektórych przypadkach lepiej jest jednak w całości przekazać to zadanie skryptowi service worker. Na przykład: aby wstępnie pobrać kilka pierwszych produktów ze strony z informacjami o produktach renderowanej po stronie klienta, może być konieczne dynamiczne wstawienie na stronie kilku tagów <link rel="prefetch"> w zależności od odpowiedzi interfejsu API. Może to zająć chwilę w głównym wątku strony i utrudnić implementację.

W takich sytuacjach użyj strategii „page to service worker”, aby przekazać zadanie pełnego pobierania z wyprzedzeniem do skryptu service worker. Tego rodzaju komunikację można realizować za pomocą metody worker.postMessage():

Ikona strony dwukierunkowej komunikacji z mechanizmem Service Worker.

Pakiet Workspace Window upraszcza ten typ komunikacji, wyodrębniając wiele szczegółów wykonywanej rozmowy.

Pobieranie z wyprzedzeniem za pomocą okna Workbox można zaimplementować w następujący sposób:

  • Na stronie wywołaj skrypt service worker, który przekazuje mu typ wiadomości, oraz listę adresów URL do pobrania z wyprzedzeniem:
const wb = new Workbox('/sw.js');
wb.register();

const prefetchResponse = await wb.messageSW({type: 'PREFETCH_URLS', urls: […]});
  • W skrypcie service worker zaimplementuj moduł obsługi wiadomości, aby wysyłać z wyprzedzeniem żądanie fetch() dla każdego adresu URL:
addEventListener('message', (event) => {
  if (event.data.type === 'PREFETCH_URLS') {
    // Fetch URLs and store them in the cache
  }
});