Natychmiastowa obsługa aktualizacji skryptu service worker

Domyślnie cykl życia skryptu service worker wymaga, aby po znalezieniu i zainstalowaniu zaktualizowanego skryptu service worker wszystkie otwarte karty, które kontroluje bieżący skrypt service worker, muszą zostać zamknięte lub przechodzące nawigację, zanim zaktualizowany skrypt service worker aktywuje się i przejmie kontrolę.

W wielu przypadkach jest to dozwolone w przyszłości, ale w niektórych przypadkach warto poinformować użytkownika o oczekującej aktualizacji skryptu service worker, a następnie zautomatyzować proces przełączania się na nowy skrypt service worker. W tym celu musisz dodać do strony kod i skrypt service worker.

Kod do umieszczenia na stronie

Poniższy kod uruchamia się we wbudowanym elemencie <script> przy użyciu modułów JavaScript zaimportowanych z hostowanej przez CDN wersji workbox-window. Rejestruje skrypt service worker za pomocą workbox-window i zareaguje, jeśli utknie w fazie oczekiwania. Po znalezieniu skryptu service worker ten kod informuje użytkownika, że dostępna jest zaktualizowana wersja witryny, i zachęca go do ponownego załadowania.

<!-- This script tag uses JavaScript modules, so the proper `type` attribute value is required -->
<script type="module">
  // This code sample uses features introduced in Workbox v6.
  import {Workbox} from 'https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-window.prod.mjs';

  if ('serviceWorker' in navigator) {
    const wb = new Workbox('/sw.js');
    let registration;

    const showSkipWaitingPrompt = async (event) => {
      // Assuming the user accepted the update, set up a listener
      // that will reload the page as soon as the previously waiting
      // service worker has taken control.
      wb.addEventListener('controlling', () => {
        // At this point, reloading will ensure that the current
        // tab is loaded under the control of the new service worker.
        // Depending on your web app, you may want to auto-save or
        // persist transient state before triggering the reload.
        window.location.reload();
      });

      // When `event.wasWaitingBeforeRegister` is true, a previously
      // updated service worker is still waiting.
      // You may want to customize the UI prompt accordingly.

      // This code assumes your app has a promptForUpdate() method,
      // which returns true if the user wants to update.
      // Implementing this is app-specific; some examples are:
      // https://open-ui.org/components/alert.research or
      // https://open-ui.org/components/toast.research
      const updateAccepted = await promptForUpdate();

      if (updateAccepted) {
        wb.messageSkipWaiting();
      }
    };

    // Add an event listener to detect when the registered
    // service worker has installed but is waiting to activate.
    wb.addEventListener('waiting', (event) => {
      showSkipWaitingPrompt(event);
    });

    wb.register();
  }
</script>

Jeśli zaakceptują zaproszenie, messageSkipWaiting() nakazuje skryptowi obsługującemu usługę oczekiwania wywołanie self.skipWaiting(), co oznacza, że rozpocznie się aktywacja. Po aktywowaniu nowy skrypt service worker przejmie kontrolę nad wszystkimi istniejącymi klientami, aktywując zdarzenie controlling w systemie workbox-window. W takim przypadku bieżąca strona zostanie ponownie załadowana przy użyciu najnowszej wersji wszystkich zasobów ze wstępnie zapisanych w pamięci podręcznej i wszystkich nowych logi routingu znalezionych w zaktualizowanym mechanizmie Service Worker.

Kod do umieszczenia w mechanizmie Service Worker

Po otrzymaniu kodu z poprzedniej sekcji strony musisz dodać do swojego mechanizmu Service Worker kod, który poinformuje go, kiedy należy pominąć fazę oczekiwania. Jeśli używasz generateSW z workbox-build i masz opcję skipWaiting ustawioną na false (domyślną), nie musisz nic robić, ponieważ poniższy kod zostanie automatycznie uwzględniony w wygenerowanym pliku skryptu service worker.

Jeśli tworzysz własny skrypt service worker – na przykład w połączeniu z jednym z narzędzi kompilacji Workbox w trybie injectManifest – musisz samodzielnie dodać ten kod:

addEventListener('message', (event) => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
});

Spowoduje to nasłuchiwanie wiadomości wysłanych do skryptu service worker z workbox-window o wartości type o wartości SKIP_WAITING, a gdy tak się stanie, wywoła funkcję self.skipWaiting(). Za wysłanie tej wiadomości odpowiada metoda messageSkipWaiting() w workbox-window pokazana we wcześniejszym przykładowym kodzie.

Czy chcesz wyświetlić prompt?

Nie jest to wzorzec, który musi stosować każda aplikacja wdrażająca skrypt service worker. Dotyczy to wybranych scenariuszy, w których brak możliwości ponownego załadowania strony przy aktualizacji skryptu service worker może spowodować nieoczekiwane zachowania. Nie ma sztywnych, prostych reguł, które decydowałyby o tym, czy wyświetlić komunikat o ponownym załadowaniu. Oto kilka sytuacji, w których może to mieć sens:

  • Intensywnie używasz wstępnego buforowania. W przypadku zasobów statycznych problemy mogą pojawić się później, jeśli w przypadku żądań nawigacji użyjesz strategii koncentrującej się na sieci lub samej sieci, a zasobów statycznych z leniwym ładowaniem. Może to powodować sytuacje, w których zasoby z obsługą wersji mogą się zmieniać, a mechanizm Service Worker nie umieścił ich w pamięci podręcznej. Umieszczenie przycisku ponownego załadowania może uniknąć nieoczekiwanych zachowań.
  • Jeśli używasz wstępnie buforowanego kodu HTML. W takim przypadku zdecydowanie rozważ oferowanie przycisku ponownego załadowania przy aktualizacjach skryptu Service Worker, ponieważ aktualizacje tego kodu HTML nie zostaną rozpoznane, dopóki zaktualizowany skrypt service worker nie przejmie kontroli.
  • Jeśli nie korzystasz głównie z pamięci podręcznej środowiska wykonawczego. Gdy zasoby są buforowane w czasie działania, nie musisz powiadamiać użytkownika, że powinien ponownie załadować zasoby. Gdy zasoby wersjonowane będą się zmieniać, zostaną one dodane do pamięci podręcznej środowiska wykonawczego, przy założeniu, że żądania nawigacji korzystają ze strategii koncentrującej się na sieci lub tylko sieci.
  • Gdy używasz strategii stale-when-revalidate, możesz rozważyć użycie modułu workbox-broadcast-update do powiadamiania użytkowników o aktualizacjach mechanizmów Service Worker.

To, czy musisz powiadamiać użytkownika o aktualizacjach skryptu service worker, zależy od aplikacji i jej unikalnych wymagań. Jeśli stwierdzisz, że użytkownicy zachowują się dziwnie po wypuszczeniu nowego mechanizmu Service Worker, jest to prawdopodobnie najlepszy sygnał, że należy ich powiadomić.