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.
Zdarzenie pobierania
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 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ść.
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.
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.
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.
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));
});
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.