Wyświetlanie

Główną cechą progresywnych aplikacji internetowych jest ich niezawodność. Są w stanie szybko ładować zasoby, utrzymywać zaangażowanie użytkowników i natychmiast przekazywać opinie, nawet w słabych warunkach sieciowych. Jak to możliwe? Dzięki zdarzeniu fetch service worker.

Obsługa przeglądarek

  • Chrome: 40
  • Edge: 17.
  • Firefox: 44.
  • Safari: 11.1

Źródło

Zdarzenie fetch pozwala nam przechwytywać wszystkie żądania sieciowe wysyłane przez PWA w zakresie skryptu service worker zarówno w przypadku żądań z tej samej domeny, jak i z innych domen. Oprócz nawigacji i żądań zasobów pobieranie z zainstalowanego mechanizmu Service Worker umożliwia renderowanie wizyt na stronie po pierwszym wczytaniu witryny bez wywołań sieciowych.

Moduł obsługi fetch odbiera wszystkie żądania z aplikacji, w tym adresy URL i nagłówki HTTP, i pozwala deweloperowi aplikacji zdecydować, jak je przetworzyć.

Skrypt service worker znajduje się między klientem a siecią.

Skrypt service worker może przekazać żądanie do sieci, odpowiedzieć wcześniej lub utworzyć nową odpowiedź. Wybór należy do Ciebie. Oto prosty przykład:

self.addEventListener("fetch", event => {
    console.log(`URL requested: ${event.request.url}`);
});

Odpowiadanie na prośbę

Gdy żądanie trafia do skryptu service worker, możesz zrobić 2 czynności: możesz go zignorować, co pozwoli mu się połączyć z siecią lub na niego zareagować. Odpowiadanie na żądania z poziomu mechanizmu Service Worker daje możliwość wyboru konkretnych elementów i sposobu ich zwrotu do aplikacji PWA, nawet gdy użytkownik jest offline.

Aby odpowiedzieć na żądanie przychodzące, wywołaj event.respondWith() z poziomu modułu obsługi zdarzeń fetch w ten sposób:

// fetch event handler in your service worker file
self.addEventListener("fetch", event => {
    const response = .... // a response or a Promise of response
    event.respondWith(response);
});

Musisz wywołać funkcję respondWith() synchronicznie i trzeba zwrócić obiekt Response. Nie możesz jednak wywołać funkcji respondWith() po zakończeniu działania modułu obsługi zdarzenia pobierania, np. w ramach wywołania asynchronicznego. Jeśli musisz poczekać na pełną odpowiedź, możesz przekazać usłudze respondWith() obietnicę, która zakończy się wraz z odpowiedzią.

Tworzenie odpowiedzi

Dzięki interfejsowi Fetch API możesz tworzyć odpowiedzi HTTP w kodzie JavaScript, które mogą być buforowane przez Cache Storage API i zwracane tak, jakby pochodziły z serwera WWW.

Aby utworzyć odpowiedź, utwórz nowy obiekt Response, ustaw jego treść i opcje, takie jak stan i nagłówki:

const simpleResponse = new Response("Body of the HTTP response");

const options = {
   status: 200,
   headers: {
    'Content-type': 'text/html'
   }
};
const htmlResponse = new Response("<b>HTML</b> content", options)

Odpowiadanie z pamięci podręcznej

Wiesz już, jak obsługiwać odpowiedzi HTTP z skryptu service worker, pora użyć interfejsu pamięci podręcznej do przechowywania zasobów na urządzeniu.

Interfejs API do przechowywania danych w pamięci podręcznej umożliwia sprawdzenie, czy żądanie otrzymane z progresywnej aplikacji internetowej jest dostępne w pamięci podręcznej. Jeśli tak jest, wyślij odpowiedź użytkownikowi respondWith(). Żeby to zrobić, musisz najpierw przeszukać pamięć podręczną. Funkcja match(), dostępna w interfejsie caches najwyższego poziomu, przeszukuje wszystkie magazyny w punkcie początkowym lub w pojedynczym otwartym obiekcie pamięci podręcznej.

Funkcja match() odbiera jako argument żądanie HTTP lub adres URL i zwraca obietnicę rozwiązaną z odpowiedzią powiązaną z odpowiednim kluczem.

// Global search on all caches in the current origin
caches.match(urlOrRequest).then(response => {
   console.log(response ? response : "It's not in the cache");
});

// Cache-specific search
caches.open("pwa-assets").then(cache => {
  cache.match(urlOrRequest).then(response => {
    console.log(response ? response : "It's not in the cache");
  });
});

Strategie buforowania

Udostępnianie plików tylko z pamięci podręcznej przeglądarki nie sprawdza się w każdym przypadku użycia. Na przykład użytkownik lub przeglądarka może wyczyścić pamięć podręczną. Dlatego warto zdefiniować własne strategie dostarczania zasobów na potrzeby progresywnej aplikacji internetowej. Nie ma ograniczenia do jednej strategii buforowania. Możesz zdefiniować różne wzorce dla różnych wzorców adresów URL. Możesz na przykład mieć jedną strategię dla minimalnej liczby zasobów interfejsu, drugą dla wywołań interfejsu API i trzecią dla adresów URL obrazów i danych. Aby to zrobić, przeczytaj dokument event.request.url w ServiceWorkerGlobalScope.onfetch i przeanalizuj go za pomocą wyrażeń regularnych lub wzorca adresu URL. (W chwili pisania funkcja Wzorzec adresu URL nie jest obsługiwana na wszystkich platformach).

Najczęstsze strategie to:

Najpierw pamięć podręczna
Najpierw wyszukuje odpowiedź z pamięci podręcznej, a jeśli jej nie znajdzie, wraca do sieci.
Najpierw sieć
Najpierw wysyła żądanie odpowiedzi od sieci, a jeśli żadna nie jest zwracana, sprawdza ją w pamięci podręcznej.
Nieaktualne podczas ponownej weryfikacji
Wysyła odpowiedź z pamięci podręcznej, natomiast w tle wysyła żądanie do najnowszej wersji i zapisuje ją w pamięci podręcznej na potrzeby następnego żądania zasobu.
Tylko sieć
Zawsze odpowiada z odpowiedzią z sieci lub komunikatem o błędzie. Pamięć podręczna nigdy nie jest sprawdzana.
Tylko pamięć podręczna
Zawsze odpowiada z pamięci podręcznej lub przekazywanymi komunikatami o błędach. Z siecią nie będziemy się nigdy konsultować. Zasoby, które będą wyświetlane z wykorzystaniem tej strategii, muszą zostać dodane do pamięci podręcznej, zanim zostaną wysłane żądania.

Najpierw zapisz w pamięci podręcznej

Korzystając z tej strategii, mechanizm Service Worker wyszukuje pasujące żądanie w pamięci podręcznej i zwraca odpowiednią odpowiedź, jeśli jest ona zapisana w pamięci podręcznej. W przeciwnym razie pobiera odpowiedź z sieci (opcjonalnie aktualizuje pamięć podręczną na potrzeby przyszłych wywołań). Jeśli nie ma odpowiedzi pamięci podręcznej ani odpowiedzi sieci, żądanie będzie powodować błąd. Wyświetlanie komponentów bez konieczności docierania do sieci trwa z reguły szybciej, dlatego w tej strategii priorytetowo traktuje się skuteczność, a nie aktualność.

Strategia Cache First

self.addEventListener("fetch", event => {
   event.respondWith(
     caches.match(event.request)
     .then(cachedResponse => {
       // It can update the cache to serve updated content on the next request
         return cachedResponse || fetch(event.request);
     }
   )
  )
});

Najpierw sieć

Ta strategia odzwierciedla strategię Cache First; sprawdza, czy żądanie może zostać zrealizowane z sieci, a jeśli nie, próbuje pobrać je z pamięci podręcznej. Zacznij od pamięci podręcznej. Jeśli nie ma odpowiedzi sieciowej ani odpowiedzi pamięci podręcznej, żądanie będzie powodować błąd. Otrzymywanie odpowiedzi z sieci trwa zwykle wolniej niż z pamięci podręcznej. Ta strategia nadaje priorytet zaktualizowanej treści, a nie jej wydajności.

Strategia Network First

self.addEventListener("fetch", event => {
   event.respondWith(
     fetch(event.request)
     .catch(error => {
       return caches.match(event.request) ;
     })
   );
});

Nieaktualne podczas ponownej weryfikacji

Opcja „Nieaktualny podczas ponownej weryfikacji” zwraca natychmiast odpowiedź z pamięci podręcznej, a następnie sprawdza dostępność aktualizacji w sieci i zastępuje znalezioną odpowiedź zapisaną w pamięci podręcznej. Ta strategia zawsze wysyła żądanie sieciowe, ponieważ nawet w przypadku znalezienia zasobu w pamięci podręcznej próbuje zaktualizować zawartość pamięci podręcznej o dane otrzymane z sieci, aby w następnym żądaniu użyć zaktualizowanej wersji. Ta strategia umożliwia więc korzystanie z szybkiego wyświetlania najpierw w ramach pamięci podręcznej i aktualizacji pamięci podręcznej w tle.

Nieaktualne podczas ponownej weryfikacji strategii

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(cachedResponse => {
        const networkFetch = fetch(event.request).then(response => {
          // update the cache with a clone of the network response
          const responseClone = response.clone()
          caches.open(url.searchParams.get('name')).then(cache => {
            cache.put(event.request, responseClone)
          })
          return response
        }).catch(function (reason) {
          console.error('ServiceWorker fetch failed: ', reason)
        })
        // prioritize cached response over network
        return cachedResponse || networkFetch
      }
    )
  )
})

Tylko sieć

Strategia „Tylko sieć” jest podobna do zachowania przeglądarek bez mechanizmu Service Worker lub interfejsu Cache Storage API. Żądania zwracają zasób tylko wtedy, gdy można go pobrać z sieci. Jest to często przydatne w przypadku zasobów takich jak żądania do interfejsu API dostępne tylko online.

Strategia „Tylko sieć”

Tylko pamięć podręczna

Strategia oparta tylko na pamięci podręcznej zapewnia, że żądania nigdy nie trafiają do sieci. odpowiedź na wszystkie przychodzące żądania jest wstępnie wypełnionym elementem pamięci podręcznej. Ten kod używa modułu obsługi zdarzeń fetch z metodą match pamięci podręcznej, aby odpowiedzieć tylko na pamięć podręczną:

self.addEventListener("fetch", event => {
   event.respondWith(caches.match(event.request));
});

Strategia oparta tylko na pamięci podręcznej.

Strategie niestandardowe

Chociaż powyższe strategie są typowymi strategiami buforowania, to Ty odpowiadasz za skrypt service worker i sposób obsługi żądań. Jeśli żaden z nich nie odpowiada Twoim potrzebom, możesz utworzyć własny.

Możesz na przykład zastosować strategię uwzględniającą czas oczekiwania w sieci, która nadaje priorytet aktualizowanym treściom, ale tylko wtedy, gdy odpowiedź nie przekracza wyznaczonego przez Ciebie progu. Możesz też scalić odpowiedź z pamięci podręcznej z odpowiedzią sieciową i stworzyć złożoną odpowiedź z wykorzystaniem skryptu service worker.

Aktualizuję zasoby

Dbanie o aktualność zasobów zapisanych w pamięci podręcznej aplikacji PWA może być wyzwaniem. Nieaktualność podczas ponownej weryfikacji strategii to jeden ze sposobów, by to osiągnąć, ale nie jedynym. W rozdziale dotyczącym aktualizacji poznasz różne techniki aktualizowania zawartości i zasobów aplikacji.

Zasoby