Obsługa żądań

Kluczowym aspektem progresywnych aplikacji internetowych jest ich niezawodność. Mogą one szybko wczytywać zasoby, utrzymywać zaangażowanie użytkowników i natychmiast przekazywać informacje zwrotne, nawet w niekorzystnych warunkach sieciowych. Jak to możliwe? Dzięki zdarzeniu service worker fetch.

Zdarzenie pobierania

Browser Support

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

Source

Zdarzenie fetch umożliwia przechwytywanie każdego żądania sieciowego wysłanego przez PWA w zakresie usługi w ramach workera usługi, zarówno w przypadku żądań tego samego pochodzenia, jak i żądań między domenami. Oprócz żądań dotyczących nawigacji i zasobów pobieranie z zainstalowanego service workera umożliwia renderowanie stron po ich pierwszym załadowaniu bez wywoływania sieci.

Obsługa fetch otrzymuje wszystkie żądania z aplikacji, w tym adresy URL i nagłówki HTTP, i pozwala deweloperowi aplikacji na podjęcie decyzji o sposobie ich przetwarzania.

Usługa robocza znajduje się między klientem a siecią.

Twój skrypt service worker może przekazać żądanie do sieci, odpowiedzieć za pomocą wcześniej zapisanej odpowiedzi 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}`);
});

Odpowiedź na prośbę

Gdy do Twojego skryptu service worker dociera żądanie, możesz zareagować na 2 sposoby: zignorować je, co spowoduje jego przekazanie do sieci, lub odpowiedzieć na nie. Odpowiadanie na żądania z serwisowego workera pozwala wybrać, co i jak ma być zwracane do PWA, nawet gdy użytkownik jest offline.

Aby odpowiedzieć na przychodzące żądanie, wywołaj funkcję event.respondWith() w ramach modułu obsługi zdarzenia fetch, na przykład 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ć metodę respondWith() w sposób synchroniczny i zwrócić obiekt Response. Nie możesz jednak wywołać funkcji respondWith() po zakończeniu działania obsługi zdarzenia pobierania, np. w ramach wywołania asynchronicznego. Jeśli musisz poczekać na pełną odpowiedź, możesz przekazać obietnicę do respondWith(), która zwraca odpowiedź.

Tworzenie odpowiedzi

Dzięki interfejsowi Fetch API możesz tworzyć odpowiedzi HTTP w kodzie JavaScript. Odpowiedzi te można zapisać w pamięci podręcznej za pomocą interfejsu Cache Storage API i zwracać tak, jakby pochodziły z serwera internetowego.

Aby utworzyć odpowiedź, utwórz nowy obiekt Response, określając 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)

Odpowiedź z pamięci podręcznej

Teraz, gdy już wiesz, jak wysyłać odpowiedzi HTTP z elementu service worker, możesz użyć interfejsu pamięci podręcznej z funkcjami buforowania, aby przechowywać zasoby na urządzeniu.

Za pomocą interfejsu API pamięci podręcznej możesz sprawdzić, czy żądanie otrzymane z PWA jest dostępne w pamięci podręcznej. Jeśli tak, możesz je przekazać do respondWith(). Aby to zrobić, musisz najpierw przeszukać pamięć podręczną. Funkcja match(), dostępna w interfejsie najwyższego poziomu caches, przeszukuje wszystkie sklepy w Twoim pochodzeniu lub jeden otwarty obiekt pamięci podręcznej.

Funkcja match() otrzymuje jako argument żądanie HTTP lub adres URL, a zwraca obietnicę, która rozwiązuje się 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 dotyczące pamięci podręcznej

Serwowanie plików tylko z pamięci podręcznej przeglądarki nie jest odpowiednie w każdym przypadku. Może to zrobić na przykład użytkownik lub przeglądarka. Dlatego musisz określić własne strategie dostarczania komponentów do swojej aplikacji internetowej. Nie musisz ograniczać się do jednej strategii dotyczącej pamięci podręcznej. Możesz zdefiniować różne reguły dla różnych wzorów adresów URL. Możesz na przykład mieć jedną strategię dla minimalnych komponentów interfejsu użytkownika, inną dla wywołań interfejsu API, a trzecią dla adresów URL obrazów i danych. Aby to zrobić, odczytaj event.request.urlServiceWorkerGlobalScope.onfetch i przeanalizuj go za pomocą wyrażeń regularnych lub wzoru adresu URL. (w momencie pisania tego artykułu wzorzec adresu URL nie jest obsługiwany na wszystkich platformach).

Najczęstsze strategie to:

Pamięć podręczna na pierwszym miejscu
Najpierw wyszukuje odpowiedź w pamięci podręcznej, a jeśli jej nie znajdzie, korzysta z sieci.
Sieć najpierw
Najpierw wysyła żądanie do sieci, a jeśli nie otrzyma odpowiedzi, sprawdza, czy nie ma jej w pamięci podręcznej.
Nieaktualne podczas ponownego potwierdzania ważności
Wysyła odpowiedź z pamięci podręcznej, a w tle wysyła żądanie najnowszej wersji i zapisuje ją w pamięci podręcznej na potrzeby kolejnego żądania zasobu.
Tylko sieć
Zawsze odpowiada odpowiedzią z sieci lub kończy działanie z błędem. Pamięć podręczna nigdy nie jest konsultowana.
Tylko pamięć podręczna
Zawsze odpowiada z pamięci podręcznej lub zwraca błąd. Sieć nigdy nie zostanie skonsultowana. Zasoby, które będą dostarczane za pomocą tej strategii, muszą zostać dodane do pamięci podręcznej przed przesłaniem żądania.

Najpierw pamięć podręczna

W ramach tej strategii skrypt service worker szuka w pamięci podręcznej pasującego żądania i zwraca odpowiednią odpowiedź, jeśli jest ona przechowywana w pamięci podręcznej. W przeciwnym razie pobiera odpowiedź z sieci (opcjonalnie aktualizując pamięć podręczną na potrzeby przyszłych wywołań). Jeśli nie ma odpowiedzi z poziomu pamięci podręcznej ani odpowiedzi z sieci, żądanie spowoduje błąd. Ponieważ dostarczanie zasobów bez korzystania z sieci jest zwykle szybsze, ta strategia stawia na skuteczność, a nie na aktualność.

Strategia „Pamięć podręczna na pierwszym miejscu”

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);
     }
   )
  )
});

Sieć na pierwszym miejscu

Ta strategia jest lustrzanym odbiciem strategii „Najpierw pamięć podręczna”. Sprawdza, czy żądanie może zostać zrealizowane z sieci, a jeśli nie, próbuje je pobrać z pamięci podręcznej. Na przykład najpierw pamięć podręczna. Jeśli nie ma odpowiedzi sieci ani odpowiedzi z poziomu pamięci podręcznej, żądanie kończy się błędem. Odbieranie odpowiedzi z sieci jest zwykle wolniejsze niż z pamięci podręcznej. Ta strategia stawia na pierwszym miejscu aktualne treści, a nie wydajność.

Strategia „Najpierw sieć”

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

Nieaktualny podczas ponownego potwierdzania ważności

Strategia nieaktualna podczas ponownej weryfikacji zwraca natychmiast odpowiedź z pamięci podręcznej, a potem sprawdza sieć pod kątem aktualizacji, zastępując pamięć podręczną, jeśli zostanie znaleziona. Ta strategia zawsze wysyła żądanie sieciowe, ponieważ nawet jeśli znajdzie zasób w pamięci podręcznej, spróbuje zaktualizować to, co było w pamięci podręcznej, za pomocą tego, co zostało odebrane z sieci, aby użyć zaktualizowanej wersji w kolejnym żądaniu. Ta strategia umożliwia szybkie wyświetlanie treści z pamięci podręcznej i aktualizowanie pamięci podręcznej w tle.

Strategia sprawdzania poprawności po przekroczeniu daty ważności

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 sieci działa podobnie jak przeglądarki bez skryptu service worker ani 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 interfejsu API tylko online.

Strategia „Tylko sieć”

Tylko pamięć podręczna

Strategia tylko z pamięci podręcznej zapewnia, że żądania nigdy nie są przesyłane do sieci. Na wszystkie przychodzące żądania odpowiada element pamięci podręcznej z wstępnie wypełnionymi danymi. Poniższy kod używa metody obsługi zdarzenia fetch z metodą match pamięci podręcznej do odpowiadania tylko z pamięci podręcznej:

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

Strategia tylko w pamięci podręcznej.

Strategie niestandardowe

Powyższe strategie są powszechnie stosowane, ale to Ty odpowiadasz za instancję roboczą usługi i sposób obsługi żądań. Jeśli żaden z nich nie odpowiada Twoim potrzebom, utwórz własny.

Możesz na przykład użyć strategii „najpierw sieć” z czasem oczekiwania, aby nadać priorytet zaktualizowanym treściom, ale tylko wtedy, gdy odpowiedź pojawi się w ramach ustawionego przez Ciebie progu. Możesz też scalić odpowiedź z poziomu pamięci podręcznej z odpowiedzią z sieci i utworzyć złożoną odpowiedź z poziomu usługi pomocniczej.

Aktualizowanie komponentów

Utrzymywanie w aktualnym stanie zasobów z poziomu pamięci podręcznej aplikacji internetowej może być trudne. Strategia ponownego sprawdzania nieaktualnych danych jest jednym ze sposobów na to, ale nie jedynym. W rozdziale poświęconym aktualizacjom poznasz różne techniki utrzymywania aktualności treści i komponentów aplikacji.

Zasoby