Tworzenie skryptu service worker

Te ćwiczenia z programowania są częścią kursu szkoleniowego „Tworzenie progresywnych aplikacji internetowych”, który został opracowany przez zespół Google Developers Training. Najwięcej korzyści przyniesie Ci ukończenie wszystkich ćwiczeń w kolejności.

Szczegółowe informacje o szkoleniu znajdziesz w omówieniu tworzenia progresywnych aplikacji internetowych.

Wprowadzenie

W tym module dowiesz się, jak utworzyć prosty skrypt service worker, i poznasz cykl życia skryptu service worker.

Czego się nauczysz

  • Utwórz podstawowy skrypt service worker, zainstaluj go i przeprowadź proste debugowanie.

Co warto wiedzieć

  • Podstawowy JavaScript i HTML
  • Pojęcia i podstawowa składnia obietnic ES2015
  • Jak włączyć konsolę programisty

Zanim zaczniesz

Pobierz lub sklonuj repozytorium pwa-training-labs z GitHub i w razie potrzeby zainstaluj wersję LTS Node.js.

Przejdź do katalogu service-worker-lab/app/ i uruchom lokalny serwer programistyczny:

cd service-worker-lab/app
npm install
node server.js

W dowolnej chwili możesz zamknąć serwer, klikając Ctrl-c.

Otwórz przeglądarkę i wejdź na stronę localhost:8081/.

Uwaga: wyrejestruj wszystkie skrypty service worker i wyczyść wszystkie ich pamięci podręczne na potrzeby hosta lokalnego, aby nie zakłócały działania modułu. W Narzędziach deweloperskich w Chrome możesz to zrobić, klikając Wyczyść dane witryny w sekcji Wyczyść pamięć na karcie Aplikacja.

Otwórz folder service-worker-lab/app/ w preferowanym edytorze tekstu. W folderze app/ będziesz tworzyć laboratorium.

Ten folder zawiera:

  • below/another.html, js/another.js, js/other.jsother.html to przykładowe zasoby, których używamy do eksperymentowania z zakresem działania service workerów.
  • styles/ folder zawiera kaskadowe arkusze stylów na potrzeby tego modułu.
  • test/ folder zawiera pliki do testowania postępów.
  • index.html to główna strona HTML naszej przykładowej witryny lub aplikacji.
  • service-worker.js to plik JavaScript używany do tworzenia naszego service workera.
  • package.json i package-lock.json śledzą pakiety węzłów używane w tym projekcie
  • server.js to prosty serwer Express, którego używamy do hostowania naszej aplikacji.

Otwórz plik service-worker.js w edytorze tekstu. Pamiętaj, że plik jest pusty. Nie dodaliśmy jeszcze żadnego kodu, który miałby być uruchamiany w usłudze Service Worker.

Otwórz plik index.html w edytorze tekstu.

W tagach <script> dodaj ten kod, aby zarejestrować service workera:

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('service-worker.js')
    .then(registration => {
      console.log('Service Worker is registered', registration);
    })
    .catch(err => {
      console.error('Registration failed:', err);
    });
  });
}

Zapisz skrypt i odśwież stronę. Konsola powinna zwrócić komunikat informujący o zarejestrowaniu komponentu service worker. W Chrome możesz sprawdzić, czy zarejestrowano service workera. W tym celu otwórz Narzędzia deweloperskie (Ctrl + Shift + I w Windows i Linux lub ⌘ + Alt + I na Macu), kliknij kartę Aplikacja, a następnie opcję Service worker. Powinien pojawić się ekran podobny do tego:

Opcjonalnie: otwórz witrynę w nieobsługiwanej przeglądarce i sprawdź, czy warunek sprawdzania obsługi działa.

Wyjaśnienie

Powyższy kod rejestruje plik service-worker.js jako service worker. Najpierw sprawdza, czy przeglądarka obsługuje service workerów. Należy to robić za każdym razem, gdy rejestrujesz service worker, ponieważ niektóre przeglądarki mogą go nie obsługiwać. Następnie kod rejestruje service worker za pomocą metody register interfejsu ServiceWorkerContainer API, która znajduje się w interfejsie Navigator okna.

navigator.serviceWorker.register(...) zwraca obietnicę, która po zarejestrowaniu service workera jest spełniana za pomocą obiektu registration. Jeśli rejestracja się nie powiedzie, obietnica zostanie odrzucona.

Zmiany stanu skryptu service worker wywołują w nim zdarzenia.

Dodawanie detektorów zdarzeń

Otwórz plik service-worker.js w edytorze tekstu.

Dodaj do service workera te detektory zdarzeń:

self.addEventListener('install', event => {
  console.log('Service worker installing...');
  // Add a call to skipWaiting here
});

self.addEventListener('activate', event => {
  console.log('Service worker activating...');
});

Zapisz plik.

Ręcznie wyrejestruj skrypt service worker i odśwież stronę, aby zainstalować i aktywować zaktualizowany skrypt service worker. W dzienniku konsoli powinna być informacja o zarejestrowaniu, zainstalowaniu i aktywowaniu nowego komponentu Service Worker.

Uwaga: log rejestracji może pojawić się w innej kolejności niż pozostałe logi (instalacji i aktywacji). Skrypt service worker działa równolegle ze stroną, więc nie możemy zagwarantować kolejności logów (log rejestracji pochodzi ze strony, a logi instalacji i aktywacji – ze skryptu service worker). Zdarzenia instalacji, aktywacji i inne zdarzenia związane z procesem roboczym usługi występują w określonej kolejności w ramach procesu roboczego usługi i zawsze powinny pojawiać się w oczekiwanej kolejności.

Wyjaśnienie

Na koniec rejestracji service worker wysyła zdarzenie install. W powyższym kodzie w install rejestrowana jest wiadomość, ale w prawdziwej aplikacji byłoby to dobre miejsce na buforowanie statycznych zasobów.

Gdy skrypt service worker zostanie zarejestrowany, przeglądarka wykrywa, czy jest on nowy (ponieważ różni się od wcześniej zainstalowanego skryptu service worker lub w przypadku tej witryny nie ma zarejestrowanego skryptu service worker). Jeśli service worker jest nowy (tak jak w tym przypadku), przeglądarka go zainstaluje.

Gdy skrypt service worker przejmie kontrolę nad stroną, wyemituje zdarzenie activate. Powyższy kod rejestruje tu komunikat, ale to zdarzenie jest często używane do aktualizowania pamięci podręcznych.

W danym momencie w określonym zakresie może być aktywny tylko jeden service worker (więcej informacji znajdziesz w artykule Exploring service worker scope). Nowo zainstalowany service worker nie jest aktywowany, dopóki istniejący service worker nie jest już używany. Dlatego wszystkie strony kontrolowane przez skrypt service worker muszą zostać zamknięte, zanim nowy skrypt service worker przejmie kontrolę. Wyrejestrowaliśmy dotychczasowy skrypt service worker, więc nowy skrypt został od razu aktywowany.

Uwaga: samo odświeżenie strony nie wystarczy, aby przekazać kontrolę nad nią nowemu skryptowi service worker, ponieważ nowa strona zostanie zażądana przed zwolnieniem bieżącej strony i nie będzie momentu, w którym stary skrypt service worker nie jest używany.

Uwaga: możesz też ręcznie aktywować nowy moduł service worker za pomocą narzędzi dla programistów w niektórych przeglądarkach oraz programowo za pomocą skipWaiting(), o czym piszemy w sekcji 3.4.

Aktualizowanie service workera

Dodaj ten komentarz w dowolnym miejscu w pliku service-worker.js:

// I'm a new service worker

Zapisz plik i odśwież stronę. Sprawdź logi w konsoli. Zauważ, że nowy proces roboczy usługi jest instalowany, ale nie aktywowany. W Chrome możesz zobaczyć oczekujący service worker na karcie Aplikacja w Narzędziach deweloperskich.

Zamknij wszystkie strony powiązane z service workerem. Następnie otwórz ponownie localhost:8081/. Dziennik konsoli powinien wskazywać, że nowy service worker został aktywowany.

Uwaga: jeśli otrzymujesz nieoczekiwane wyniki, upewnij się, że pamięć podręczna HTTP jest wyłączona w narzędziach dla programistów.

Wyjaśnienie

Przeglądarka wykrywa różnicę w bajtach między nowym a dotychczasowym plikiem service worker (z powodu dodanego komentarza), więc instaluje nowy service worker. Ponieważ w danym momencie może być aktywny tylko 1 service worker (w danym zakresie), nawet jeśli nowy service worker jest zainstalowany, nie zostanie aktywowany, dopóki istniejący service worker nie będzie już używany. Zamykając wszystkie strony kontrolowane przez stary skrypt service worker, możemy aktywować nowy skrypt.

Pominięcie fazy oczekiwania

Nowy service worker może zostać aktywowany od razu, nawet jeśli jest już obecny inny service worker, poprzez pominięcie fazy oczekiwania.

service-worker.js dodaj wywołanie funkcji skipWaiting w detektorze zdarzeń install:

self.skipWaiting();

Zapisz plik i odśwież stronę. Zwróć uwagę, że nowy service worker jest instalowany i aktywuje się natychmiast, mimo że poprzedni service worker był aktywny.

Wyjaśnienie

Metoda skipWaiting() umożliwia aktywowanie elementu service worker natychmiast po zakończeniu instalacji. Detektor zdarzeń instalacji to typowe miejsce na wywołanie skipWaiting(), ale można go wywołać w dowolnym momencie w trakcie fazy oczekiwania lub przed nią. Więcej informacji o tym, kiedy i jak używać symbolu skipWaiting(), znajdziesz w tej dokumentacji. W pozostałej części ćwiczenia możemy teraz testować nowy kod skryptu service worker bez ręcznego wyrejestrowywania skryptu service worker.

Więcej informacji

Skrypty service worker mogą pełnić funkcję serwera proxy między aplikacją internetową a siecią.

Dodajmy odbiornik pobierania, aby przechwytywać żądania z naszej domeny.

Dodaj do pliku service-worker.js ten kod:

self.addEventListener('fetch', event => {
  console.log('Fetching:', event.request.url);
});

Zapisz skrypt i odśwież stronę, aby zainstalować i aktywować zaktualizowany service worker.

Sprawdź konsolę i zobacz, że nie zarejestrowano żadnych zdarzeń pobierania. Odśwież stronę i ponownie sprawdź konsolę. Tym razem powinny być widoczne zdarzenia pobierania dotyczące strony i jej zasobów (np. CSS).

Kliknij linki Inna strona, Kolejna stronaWstecz.

W konsoli zobaczysz zdarzenia pobierania dotyczące każdej strony i jej komponentów. Czy wszystkie logi są sensowne?

Uwaga: jeśli odwiedzisz stronę i nie wyłączysz pamięci podręcznej HTTP, zasoby CSS i JavaScript mogą być przechowywane lokalnie w pamięci podręcznej. W takim przypadku nie zobaczysz zdarzeń pobierania tych zasobów.

Wyjaśnienie

Service worker otrzymuje zdarzenie pobierania dla każdego żądania HTTP wysyłanego przez przeglądarkę, które mieści się w jego zakresie. Obiekt fetch event zawiera żądanie. Nasłuchiwanie zdarzeń pobierania w service workerze jest podobne do nasłuchiwania zdarzeń kliknięcia w DOM. W naszym kodzie, gdy wystąpi zdarzenie pobierania, rejestrujemy w konsoli żądany adres URL (w praktyce możemy też utworzyć i zwrócić własną niestandardową odpowiedź z dowolnymi zasobami).

Dlaczego podczas pierwszego odświeżenia nie zarejestrowano żadnych zdarzeń pobierania? Domyślnie zdarzenia pobierania ze strony nie przechodzą przez skrypt service worker, chyba że samo żądanie strony przeszło przez skrypt service worker. Zapewnia to spójność w witrynie – jeśli strona wczytuje się bez skryptu service worker, to samo dotyczy jej zasobów podrzędnych.

Więcej informacji

Kod rozwiązania

Aby uzyskać kopię działającego kodu, otwórz folder 04-intercepting-network-requests/.

Skrypty service worker mają zakres. Zakres skryptu service worker określa, z których ścieżek skrypt service worker przechwytuje żądania.

Znajdź zakres

Zaktualizuj kod rejestracyjny w index.html, wpisując:

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('service-worker.js')
    .then(registration => {
      console.log('SW registered with scope:', registration.scope);
    })
    .catch(err => {
      console.error('Registration failed:', err);
    });
  });
}

Odśwież przeglądarkę. Zwróć uwagę, że konsola pokazuje zakres skryptu service worker (w tym przypadku jest to http://localhost:8081/).

Wyjaśnienie

Obietnica zwrócona przez register() jest rozwiązywana w obiekcie rejestracji, który zawiera zakres service workera.

Domyślny zakres to ścieżka do pliku service worker, która obejmuje wszystkie podrzędne katalogi. Dlatego service worker w katalogu głównym aplikacji kontroluje żądania ze wszystkich plików w aplikacji.

Przenoszenie skryptu service worker

Przenieś plik service-worker.js do katalogu below/ i zaktualizuj adres URL service workera w kodzie rejestracji w pliku index.html.

Wyrejestruj bieżący skrypt service worker w przeglądarce i odśwież stronę.

W konsoli widać, że zakres modułu service worker to teraz http://localhost:8081/below/. W Chrome możesz też sprawdzić zakres skryptu service worker na karcie aplikacji w Narzędziach deweloperskich:

Na stronie głównej kliknij Inna strona, Kolejna stronaWstecz. Które żądania pobierania są rejestrowane? Które nie?

Wyjaśnienie

Domyślny zakres instancji roboczej usługi to ścieżka do pliku instancji roboczej usługi. Plik service worker znajduje się teraz w folderze below/, więc to jest jego zakres. Konsola rejestruje teraz tylko zdarzenia pobierania dla adresów another.html, another.cssanother.js, ponieważ są to jedyne zasoby w zakresie skryptu service worker.

Ustawianie dowolnego zakresu

Przenieś service worker z powrotem do głównego katalogu projektu (app/) i zaktualizuj adres URL service worker w kodzie rejestracji w index.html.

Skorzystaj z dokumentacji w MDN, aby ustawić zakres service workera na katalog below/ za pomocą parametru opcjonalnego w register().

Wyrejestruj skrypt service worker i odśwież stronę. Kliknij Inna strona, Kolejna stronaWstecz.

Konsola ponownie pokazuje, że zakres skryptu service worker to teraz http://localhost:8081/below/, i rejestruje zdarzenia pobierania tylko w przypadku another.html, another.cssanother.js.

Wyjaśnienie

Podczas rejestracji można ustawić dowolny zakres, przekazując dodatkowy parametr, np.:

navigator.serviceWorker.register('/service-worker.js', {
  scope: '/kitten/'
});

W przykładzie powyżej zakres skryptu service worker jest ustawiony na /kitten/. Skrypt service worker przechwytuje żądania ze stron w domenach /kitten//kitten/lower/, ale nie ze stron takich jak /kitten czy /.

Uwaga: nie możesz ustawić dowolnego zakresu, który jest powyżej rzeczywistej lokalizacji service workera. Jeśli jednak Twój proces roboczy serwera jest aktywny na kliencie obsługiwanym za pomocą nagłówka Service-Worker-Allowed, możesz określić maksymalny zakres dla tego procesu roboczego serwera powyżej jego lokalizacji.

Więcej informacji

Kod rozwiązania

Aby uzyskać kopię działającego kodu, otwórz folder solution/.

Masz już działający prosty service worker i rozumiesz jego cykl życia.

Więcej informacji

Cykl życia service workera

Aby zobaczyć wszystkie codelaby w ramach szkolenia PWA, zapoznaj się z codelabem wprowadzającym.