Trudno jest wiedzieć, co robią skrypty service worker, bez znajomości ich cyklu życia. Ich wewnętrzne działanie będzie niejasne, a nawet przypadkowe. Warto pamiętać, że podobnie jak w przypadku innych interfejsów API przeglądarki, zachowania usług workera są dobrze zdefiniowane i określone, co umożliwia tworzenie aplikacji offline, a także ułatwia wprowadzanie aktualizacji bez zakłócania wygody użytkowników.
Zanim zaczniesz korzystać z Workbox, warto poznać cykl życia usługi, aby zrozumieć, jak działa Workbox.
Definiowanie terminów
Zanim przejdziemy do omówienia cyklu życia usługi, warto zdefiniować kilka pojęć związanych z jego działaniem.
Kontrola i zakres
Pojęcie kontroli jest kluczowe dla zrozumienia działania usług. Strona opisana jako sterowana przez skrypt service worker to strona, która pozwala skryptowi service worker przechwytywać żądania sieciowe w jej imieniu. Skrypt service worker jest obecny i może wykonywać działania na stronie w określonym zakresie.
Zakres
Zakres usługi jest określany przez jej lokalizację na serwerze WWW.
Jeśli skrypt service worker działa na stronie znajdującej się pod adresem /subdir/index.html
i znajduje się pod adresem /subdir/sw.js
, jego zakres to /subdir/
.
Aby zobaczyć, jak działa zakres, zapoznaj się z tym przykładem:
- Otwórz stronę https://service-worker-scope-viewer.glitch.me/subdir/index.html.
Pojawi się komunikat, że żaden skrypt service worker nie kontroluje strony.
Strona ta rejestruje jednak skrypt service worker z adresu
https://service-worker-scope-viewer.glitch.me/subdir/sw.js
. - Odśwież stronę. Skrypt service worker został zarejestrowany i jest teraz aktywny, więc kontroluje stronę. Zostanie wyświetlony formularz z zakresem, bieżącym stanem i adresem URL usługi. Uwaga: konieczność ponownego wczytania strony nie ma nic wspólnego z zakresem, a z cyklem życia usługi, co zostanie wyjaśnione później.
- Otwórz stronę https://service-worker-scope-viewer.glitch.me/index.html. Mimo że skrypt service worker został zarejestrowany w tym źródle, nadal wyświetla się komunikat, że nie ma aktualnego skryptu service worker. Dzieje się tak, ponieważ ta strona nie znajduje się w zakresie działania zarejestrowanego skryptu service worker.
Zakres określa, które strony steruje skrypt service worker.
W tym przykładzie oznacza to, że skrypt service worker wczytany z /subdir/sw.js
może kontrolować tylko strony znajdujące się w /subdir/
lub w jej poddrzewie.
Powyższe informacje opisują sposób działania zakresu domyślnego, ale maksymalny dozwolony zakres można zastąpić, ustawiając nagłówek odpowiedzi Service-Worker-Allowed
, a także przekazując opcję scope
do metody register
.
Jeśli nie ma bardzo dobrego powodu, aby ograniczyć zakres skryptu service worker do podzbioru źródła, wczytaj go z katalogu głównego serwera WWW, aby jego zakres był jak najszerszy. Nie przejmuj się nagłówkiem Service-Worker-Allowed
. W ten sposób wszystko będzie prostsze.
Klient
Gdy mówimy, że skrypt service worker kontroluje stronę, tak naprawdę kontroluje on klienta.
Klientem jest każda otwarta strona, której adres URL mieści się w zakresie działania danego pracownika usługi.
Dotyczy to w szczególności instancji WindowClient
.
Cykl życia nowego serwisowego workera
Aby usługa workera mogła kontrolować stronę, musi najpierw zostać uruchomiona. Zacznijmy od tego, co się dzieje, gdy w witrynie bez aktywnych usług publikujemy nową usługę.
Rejestracja
Rejestracja to pierwszy etap cyklu życia skryptu service worker:
<!-- In index.html, for example: -->
<script>
// Don't register the service worker
// until the page has fully loaded
window.addEventListener('load', () => {
// Is service worker available?
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(() => {
console.log('Service worker registered!');
}).catch((error) => {
console.warn('Error registering service worker:');
console.warn(error);
});
}
});
</script>
Ten kod działa w wątku głównym i wykonuje te czynności:
- Podczas pierwszej wizyty użytkownika w witrynie nie jest rejestrowany żaden pracownik usługi, dlatego przed zarejestrowaniem pracownika poczekaj, aż strona zostanie w pełni załadowana. Zapobiega to rywalizacji o przepustowość, jeśli usługa robocza przechowuje coś w pamięci podręcznej.
- Chociaż skrypt service worker jest dobrze obsługiwany, szybkie sprawdzenie pomoże uniknąć błędów w przeglądarkach, w których nie jest obsługiwany.
- Po całkowitym załadowaniu strony i jeśli skrypt service worker jest obsługiwany, zarejestruj
/sw.js
.
Oto najważniejsze informacje:
- Usługa robocza jest dostępna tylko przez HTTPS lub localhost.
- Jeśli zawartość usługi zawiera błędy składni, rejestracja się nie powiedzie, a usługa zostanie odrzucona.
- Przypomnienie: skrypty service worker działają w określonym zakresie. W tym przypadku zakres obejmuje całą domenę, ponieważ została ona załadowana z katalogu głównego.
- Gdy rozpoczyna się rejestracja, stan pracownika usługi jest ustawiony na
'installing'
.
Po zakończeniu rejestracji rozpocznie się instalacja.
Instalacja
Po zarejestrowaniu usługa robocza uruchamia zdarzenie install
.
Funkcja install
jest wywoływana tylko raz na jednego pracownika obsługi. Nie zostanie ponownie wywołana, dopóki nie zostanie zaktualizowana.
Wywołanie zwrotne dla zdarzenia install
można zarejestrować w zakresie pracownika za pomocą addEventListener
:
// /sw.js
self.addEventListener('install', (event) => {
const cacheKey = 'MyFancyCacheName_v1';
event.waitUntil(caches.open(cacheKey).then((cache) => {
// Add all the assets in the array to the 'MyFancyCacheName_v1'
// `Cache` instance for later use.
return cache.addAll([
'/css/global.bc7b80b7.css',
'/css/home.fe5d0b23.css',
'/js/home.d3cc4ba4.js',
'/js/jquery.43ca4933.js'
]);
}));
});
Spowoduje to utworzenie nowej instancji Cache
i wstępnej pamięci podręcznej komponentów.
O wstępnym zapisywaniu danych będziemy mówić jeszcze nie raz, więc skupmy się na roli event.waitUntil
. event.waitUntil
akceptuje obietnicę i czeka, aż zostanie ona spełniona.
W tym przykładzie obietnica wykonuje 2 funkcje asynchroniczne:
- Tworzy nową instancję
Cache
o nazwie'MyFancyCache_v1'
. - Po utworzeniu pamięci podręcznej tablica adresów URL zasobów jest wstępnie zapisywana w pamięci podręcznej za pomocą asynchronicznej metody
addAll
.
Instalacja kończy się niepowodzeniem, jeśli obietnice przekazane do event.waitUntil
są odrzucane.
W takim przypadku pracownik usługi jest odrzucany.
Jeśli obietnice spełnią się, instalacja zakończy się powodzeniem, a stan usługi workera zmieni się na 'installed'
i usługa zostanie aktywowana.
Aktywacja
Jeśli rejestracja i instalacja się powiedzie, usługa zostanie uruchomiona, a jej stan zmieni się na 'activating'
Podczas aktywacji w usługowym workerze może być wykonywana praca w ramach zdarzenia activate
.
Typowym zadaniem w tym przypadku jest usuwanie starych pamięci podręcznych, ale w przypadku zupełnie nowego service workera nie jest to na razie istotne. Więcej informacji na ten temat znajdziesz w sekcji poświęconej aktualizacjom service workera.
W przypadku nowych usług activate
uruchamia się natychmiast po zakończeniu działania install
.
Po zakończeniu aktywacji stan usługi jest 'activated'
.
Pamiętaj, że domyślnie nowy skrypt service worker nie zacznie kontrolować strony, dopóki nie nastąpi przejście do innej strony lub jej odświeżenie.
Obsługa aktualizacji usługi
Po wdrożeniu pierwszego service workera prawdopodobnie trzeba będzie go później zaktualizować. Aktualizacja może być na przykład wymagana, jeśli nastąpią zmiany w obsługiwaniu żądań lub logice precachingu.
Kiedy odbywają się aktualizacje
Przeglądarki sprawdzają dostępność aktualizacji dla service workera, gdy:
- Użytkownik przechodzi na stronę w zakresie działania skryptu service worker.
navigator.serviceWorker.register()
jest wywoływany z adresem URL innym niż adres zainstalowanego obecnie serwisowego workera – nie zmieniaj adresu URL serwisowego workera.navigator.serviceWorker.register()
jest wywoływany pod tym samym adresem URL co zainstalowany serwis worker, ale z innym zakresem. Ponownie, unikaj tego, utrzymując zakres na poziomie źródeł.- Gdy w ciągu ostatnich 24 godzin zostały wywołane zdarzenia takie jak
'push'
lub'sync'
– ale nie martw się jeszcze tymi zdarzeniami.
Jak odbywają się aktualizacje
Ważne jest, kiedy przeglądarka aktualizuje pracownika usługi, ale równie istotne jest „jak”. Zakładając, że adres URL lub zakres usługi nie uległ zmianie, aktualnie zainstalowana usługa zostanie zaktualizowana do nowej wersji tylko wtedy, gdy zmieni się jej zawartość.
Przeglądarki wykrywają zmiany na kilka sposobów:
- Wszelkie zmiany skryptów (bit po bicie) żądane przez
importScripts
(w stosownych przypadkach). - wszelkie zmiany w kodzie najwyższego poziomu usługi, które wpływają na wygenerowany przez przeglądarkę odcisk palca;
W tym przypadku przeglądarka wykonuje większość pracy. Aby zapewnić przeglądarce wszystko, czego potrzebuje do niezawodnego wykrywania zmian w zawartości usługi, nie informuj pamięci podręcznej HTTP o tym, że ma ona przechowywać dane, i nie zmieniaj nazwy pliku. Przeglądarka automatycznie sprawdza dostępność aktualizacji, gdy następuje przejście na nową stronę w zakresie usługi.
Ręczne uruchamianie sprawdzania dostępności aktualizacji
Jeśli chodzi o aktualizacje, logika rejestracji zwykle nie powinna się zmieniać. Wyjątkiem może być sytuacja, gdy sesje w witrynie trwają długo. Może się to zdarzyć w przypadku aplikacji jednostronnych, w których żądania nawigacji są rzadkie, ponieważ na początku cyklu życia aplikacji występuje zwykle 1 żądanie nawigacji. W takich sytuacjach można uruchomić ręczną aktualizację w wątku głównym:
navigator.serviceWorker.ready.then((registration) => {
registration.update();
});
W przypadku tradycyjnych witryn lub w każdym przypadku, gdy sesje użytkowników nie są długotrwałe, ręczne uruchamianie aktualizacji prawdopodobnie nie jest konieczne.
Instalacja
Jeśli do generowania zasobów statycznych używasz narzędzia do tworzenia pakietów, nazwy tych zasobów będą zawierać znaki hash, np. framework.3defa9d2.js
.
Załóżmy, że niektóre z tych zasobów są wcześniej przechowywane w pamięci podręcznej, aby można było z nich korzystać offline.
Wymaga to zaktualizowania usługi w tle, aby zaktualizowane zasoby zostały zapisane w pamięci podręcznej:
self.addEventListener('install', (event) => {
const cacheKey = 'MyFancyCacheName_v2';
event.waitUntil(caches.open(cacheKey).then((cache) => {
// Add all the assets in the array to the 'MyFancyCacheName_v2'
// `Cache` instance for later use.
return cache.addAll([
'/css/global.ced4aef2.css',
'/css/home.cbe409ad.css',
'/js/home.109defa4.js',
'/js/jquery.38caf32d.js'
]);
}));
});
W porównaniu z pierwszym przykładem zdarzenia install
występują 2 różnice:
- Utworzona zostaje nowa instancja
Cache
z kluczem'MyFancyCacheName_v2'
. - Zmieniły się nazwy zasobów z cache'em.
Pamiętaj, że zaktualizowany skrypt service worker jest instalowany obok poprzedniego. Oznacza to, że stary serwis worker nadal kontroluje wszystkie otwarte strony, a po zainstalowaniu nowy przechodzi w stan oczekiwania, dopóki nie zostanie aktywowany.
Domyślnie nowy serwis worker zostanie aktywowany, gdy żaden klient nie będzie kontrolowany przez stary. Dzieje się tak, gdy wszystkie otwarte karty danej witryny są zamknięte.
Aktywacja
Gdy zaktualizowany serwis worker zostanie zainstalowany i zakończy się faza oczekiwania, zostanie on aktywowany, a stary serwis worker zostanie odrzucony.
Typowym zadaniem do wykonania w zaktualizowanym zdarzeniu skryptu service workera activate
jest usuwanie starych pamięci podręcznych.
Usuń stare pamięci podręczne, uzyskując klucze wszystkich otwartych wystąpień Cache
za pomocą caches.keys
, a następnie usuwając pamięci podręczne, które nie znajdują się na zdefiniowanej liście dozwolonych za pomocą caches.delete
:
self.addEventListener('activate', (event) => {
// Specify allowed cache keys
const cacheAllowList = ['MyFancyCacheName_v2'];
// Get all the currently active `Cache` instances.
event.waitUntil(caches.keys().then((keys) => {
// Delete all caches that aren't in the allow list:
return Promise.all(keys.map((key) => {
if (!cacheAllowList.includes(key)) {
return caches.delete(key);
}
}));
}));
});
Starsze pamięci podręczne nie są automatycznie porządkowane.
Musimy to zrobić sami, ponieważ istnieje ryzyko przekroczenia limitów miejsca na dane.
Ponieważ 'MyFancyCacheName_v1'
z pierwszego workera usługi jest nieaktualny, lista zezwalająca na pamięć podręczną jest aktualizowana, aby zawierać 'MyFancyCacheName_v2'
, co powoduje usunięcie pamięci podręcznej o innej nazwie.
Zdarzenie activate
zostanie zakończone po usunięciu starej pamięci podręcznej.
W tym momencie nowy skrypt service worker przejmie kontrolę nad stroną, zastępując stary.
Cykl życia trwa dalej
Niezależnie od tego, czy do obsługi wdrażania i aktualizowania usług workera służy Workbox, czy też interfejs Service Worker API jest używany bezpośrednio, warto poznać cykl życia usług workera. Dzięki temu działanie usług w tle powinno wydawać się bardziej logiczne niż tajemnicze.
Jeśli chcesz dowiedzieć się więcej na ten temat, przeczytaj ten artykuł Jake’a Archibalda. W całym cyklu życia usługi jest mnóstwo niuansów, ale można je poznać, a ta wiedza przyda się podczas korzystania z Workboxa.