Domyślnie nowe mechanizmy Service Worker

tl;dr

Od wersji 68 przeglądarki Chrome żądania HTTP, które sprawdzają, czy skrypt usługi roboczej został zaktualizowany, nie będą już domyślnie obsługiwane przez pamięć podręczną HTTP. Rozwiązanie to pozwala ominąć częsty problem deweloperów, który polega na tym, że niezamierzone ustawienie nagłówka Cache-Control w skrypcie usługi może powodować opóźnione aktualizacje.

Jeśli masz już wyłączone buforowanie HTTP dla skryptu /service-worker.js, wyświetlając go z użyciem Cache-Control: max-age=0, nie zauważysz żadnych zmian ze względu na nowe domyślne zachowanie.

Dodatkowo od Chrome 78 porównywanie bajt po bajcie będzie stosowane do skryptów wczytywanych w usługach workera za pomocą importScripts(). Każda zmiana wprowadzona w zaimportowanym skrypcie spowoduje przeprowadzenie procesu aktualizacji usługi w tle, tak jak w przypadku zmiany w usługach w tle najwyższego poziomu.

Tło

Za każdym razem, gdy przechodzisz na nową stronę, która jest objęta zakresem usługi workera, wywołujesz jawnie registration.update() z JavaScriptu lub gdy usługa workera zostanie „obudzona” za pomocą zdarzenia push lub sync, przeglądarka równolegle poprosi o zasób JavaScriptu, który został pierwotnie przekazany do wywołania navigator.serviceWorker.register(), aby sprawdzić, czy nie ma aktualizacji skryptu usługi workera.

W tym artykule przyjmijmy, że jego adres URL to /service-worker.js i że zawiera on pojedyncze wywołanie importScripts(), które wczytuje dodatkowy kod uruchamiany w ramach usługi:

// Inside our /service-worker.js file:
importScripts('path/to/import.js');

// Other top-level code goes here.

Co się zmienia?

Przed wersją 68 przeglądarki Chrome żądanie aktualizacji /service-worker.js było wysyłane za pomocą pamięci podręcznej HTTP (podobnie jak większość pobierania). Oznacza to, że jeśli skrypt został pierwotnie wysłany za pomocą Cache-Control: max-age=600, aktualizacje w ciągu kolejnych 600 sekund (10 minut) nie będą wysyłane do sieci, więc użytkownik może nie otrzymać najnowszej wersji usługi. Jeśli jednak max-age jest większa niż 86 400 (24 godziny), zostanie potraktowana tak, jakby wynosiła 86 400, aby użytkownicy nie byli zmuszeni do korzystania z konkretnej wersji przez całą wieczność.

Począwszy od wersji 68 pamięć podręczna HTTP będzie ignorowana podczas żądania aktualizacji kodu usługi, więc istniejące aplikacje internetowe mogą zwiększyć częstotliwość żądań kodu usługi. Żądania dotyczące importScripts będą nadal wysyłane przez pamięć podręczną HTTP. Jest to jednak tylko domyślna opcja. Dostępna jest nowa opcja rejestracji updateViaCache, która umożliwia kontrolowanie tego zachowania.

updateViaCache

Deweloperzy mogą teraz przekazywać nową opcję podczas wywoływania funkcji navigator.serviceWorker.register(): parametr updateViaCache. Może przyjmować jedną z 3 wartości: 'imports', 'all' lub 'none'.

Wartości te określają, czy i w jaki sposób standardowa pamięć podręczna HTTP przeglądarki jest używana podczas wysyłania żądania HTTP w celu sprawdzenia, czy zasoby usługi workera są aktualne.

  • Gdy ta opcja ma wartość 'imports', pamięć podręczna HTTP nigdy nie będzie konsultowana podczas sprawdzania aktualizacji skryptu /service-worker.js, ale będzie konsultowana podczas pobierania sprowadzonych skryptów (w naszym przykładzie skryptu path/to/import.js). Jest to ustawienie domyślne i odpowiada sposobowi działania w Chrome 68.

  • Gdy wartość to 'all', podczas wysyłania żądań do skryptu najwyższego poziomu /service-worker.js oraz skryptów zaimportowanych do service workera, np. path/to/import.js, będzie konsultowana pamięć podręczna HTTP. Ta opcja odpowiada poprzedniemu działaniu Chrome w wersjach starszych niż 68.

  • Gdy ta wartość jest ustawiona na 'none', pamięć podręczna HTTP nie będzie konsultowana podczas wysyłania żądań do /service-worker.js najwyższego poziomu ani do żadnych zaimportowanych skryptów, takich jak hipotetyczny path/to/import.js.

Na przykład poniższy kod spowoduje zarejestrowanie pracownika usługi i zapewni, że pamięć podręczna HTTP nigdy nie będzie konsultowana podczas sprawdzania aktualizacji skryptu /service-worker.js ani skryptów, do których odwołuje się /service-worker.js w ramach /service-worker.js:importScripts()

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js', {
    updateViaCache: 'none',
    // Optionally, set 'scope' here, if needed.
  });
}

Sprawdzanie dostępności aktualizacji zaimportowanych skryptów

Przed wersją Chrome 78 każdy skrypt usługi wczytywany za pomocą funkcji importScripts() był pobierany tylko raz (najpierw sprawdzano pamięć podręczną HTTP lub sieć, w zależności od konfiguracji updateViaCache). Po tym początkowym pobraniu przeglądarka przechowuje dane wewnętrznie i nigdy ich ponownie nie pobiera.

Jedynym sposobem na wymuszenie na już zainstalowanym serwisie workerze pobrania zmian w importowanym skrypcie było zmodyfikowanie adresu URL skryptu, zwykle przez dodanie wartości semver (np. importScripts('https://example.com/v1.1.0/index.js')) lub dodanie hasha zawartości (np. importScripts('https://example.com/index.abcd1234.js')). Efektem ubocznym zmiany zaimportowanego adresu URL jest zmiana zawartości skryptu serwisowego najwyższego poziomu, co z kolei powoduje uruchomienie procesu aktualizacji serwisowego workera.

Począwszy od wersji 78 Chrome, za każdym razem, gdy zostanie przeprowadzone sprawdzanie aktualizacji pliku service worker na najwyższym poziomie, jednocześnie zostaną przeprowadzone kontrole, aby określić, czy zmieniła się zawartość dowolnego zaimportowanego skryptu. W zależności od użytych nagłówków Cache-Control zaimportowane sprawdzenia skryptu mogą być wykonywane przez pamięć podręczną HTTP, jeśli parametr updateViaCache ma wartość 'all' lub 'imports' (co jest wartością domyślną), albo bezpośrednio przez sieć, jeśli parametr updateViaCache ma wartość 'none'.

Jeśli sprawdzenie aktualizacji zaimportowanego skryptu wykaże różnicę w bajtach w porównaniu z danymi wcześniej zapisanymi przez usługę workera, spowoduje to pełny proces aktualizacji usługi workera, nawet jeśli plik usługi workera najwyższego poziomu pozostanie taki sam.

Zachowanie Chrome 78 jest zgodne z tym, co wdrożył Firefox kilka lat temu w Firefox 56. Safari już stosuje takie działanie.

Co muszą zrobić deweloperzy?

Jeśli skutecznie wyłączysz buforowanie HTTP dla skryptu /service-worker.js, wyświetlając go z wartością Cache-Control: max-age=0 (lub podobną), nie zauważysz żadnych zmian ze względu na nowe domyślne zachowanie.

Jeśli skrypt /service-worker.js jest obsługiwany z włączonym buforowaniem HTTP (czy to celowo, czy też dlatego, że jest to wartość domyślna dla środowiska hostingu), możesz zacząć obserwować wzrost liczby dodatkowych żądań HTTP do /service-worker.js wysyłanych do Twojego serwera. Są to żądania, które były wcześniej obsługiwane przez bufor HTTP. Jeśli chcesz nadal zezwalać na to, aby wartość nagłówka Cache-Control wpływała na aktualność wartości /service-worker.js, musisz zacząć jawnie ustawiać wartość updateViaCache: 'all' podczas rejestrowania pracownika usługi.

Ponieważ może być wielu użytkowników korzystających ze starszych wersji przeglądarek, warto nadal ustawiać nagłówek HTTP Cache-Control: max-age=0 w skryptach usług, nawet jeśli nowsze przeglądarki mogą je ignorować.

Deweloperzy mogą wykorzystać tę możliwość, aby zdecydować, czy chcą, aby zaimportowane skrypty były teraz wykluczone z użytkowania pamięci podręcznej HTTP, i w odpowiednich przypadkach dodać do rejestracji service workera parametr updateViaCache: 'none'.

Wyświetlanie zaimportowanych skryptów

Począwszy od wersji 78 Chrome deweloperzy mogą zauważyć więcej przychodzących żądań HTTP dotyczących zasobów wczytywanych za pomocą importScripts(), ponieważ będą one teraz sprawdzane pod kątem aktualizacji.

Jeśli chcesz uniknąć tego dodatkowego ruchu HTTP, ustaw długotrwałe nagłówki Cache-Control podczas wyświetlania skryptów, które zawierają semver lub hashe w swoich adresach URL, i polegaj na domyślnym zachowaniu updateViaCache w 'imports'.

Jeśli chcesz, aby zaimportowane skrypty były sprawdzane pod kątem częstych aktualizacji, wyświetlaj je za pomocą Cache-Control: max-age=0 lub użyj updateViaCache: 'none'.

Więcej informacji

The Service Worker Lifecycle” (Cykl życia usługi) i „Caching best practices & max-age gotchas” (Sprawdzone metody dotyczące pamięci podręcznej i maksimum wieku) Jake’a Archibalda to artykuły, które warto przeczytać wszystkim deweloperom, którzy wdrażają treści do sieci.