W niektórych przypadkach może być konieczne, aby usługa komunikowała się aktywnie z aktywnymi kartami, nad którymi ma kontrolę, w celu poinformowania o określonym zdarzeniu. Przykłady:
- Informowanie strony o zainstalowaniu nowej wersji pracownika usługi, aby mogła ona wyświetlić użytkownikowi przycisk „Aktualizuj, aby odświeżyć”, umożliwiający natychmiastowy dostęp do nowej funkcji.
- Poinformowanie użytkownika o zmianie danych w pamięci podręcznej, która nastąpiła po stronie service workera, przez wyświetlenie komunikatu, takiego jak „Aplikacja jest teraz gotowa do pracy w trybie offline” lub „Dostępna nowa wersja treści”.
Tego typu przypadki użycia będziemy nazywać „aktualizacjami transmisji”, które nie muszą otrzymywać wiadomości ze strony, aby rozpocząć komunikację. W tym przewodniku omówimy różne sposoby implementowania tego typu komunikacji między stronami a usługami w tle, korzystając ze standardowych interfejsów API przeglądarki i biblioteki Workbox.
Przypadki produkcyjne
Tinder
PWA Tinder wykorzystuje workbox-window
do nasłuchiwania ważnych momentów w cyklu życia instancji roboczych usługi („zainstalowano”, „kontrolowane” i „aktywowane”). Dzięki temu, gdy pojawia się nowy skrypt service worker, wyświetla się baner „Dostępna aktualizacja”, umożliwiając odświeżenie PWA i uzyskiwanie dostępu do najnowszych funkcji:
Squoosh
W przypadku PWA w Squoosh, gdy usługa w tle ma w pamięci podręcznej wszystkie niezbędne zasoby do działania offline, wysyła do strony wiadomość, aby wyświetlić powiadomienie „Gotowe do pracy offline”, informujące użytkownika o tej funkcji:
Korzystanie z Workbox
Nasłuchiwanie zdarzeń cyklu życia usługi workera
workbox-window
udostępnia prosty interfejs do nasłuchiwania ważnych zdarzeń cyklu życia skryptu service worker.
Biblioteka korzysta z interfejsów API po stronie klienta, takich jak updatefound
i statechange, oraz udostępnia obiekt workbox-window
z odbiorcami zdarzeń wyższego poziomu, co ułatwia użytkownikowi korzystanie z tych zdarzeń.
Poniższy kod strony umożliwia wykrywanie każdego zainstalowania nowej wersji usługi działającej w tle, aby można było poinformować o tym użytkownika:
const wb = new Workbox('/sw.js');
wb.addEventListener('installed', (event) => {
if (event.isUpdate) {
// Show "Update App" banner
}
});
wb.register();
Informuj stronę o zmianach w danych pamięci podręcznej
Pakiet Workbox
workbox-broadcast-update
zapewnia standardowy sposób powiadamiania klientów okien o zaktualizowaniu odpowiedzi z pamięci podręcznej. Jest ona najczęściej używana razem ze strategią StaleWhileRevalidate.
Aby przesyłać aktualizacje, dodaj opcję broadcastUpdate.BroadcastUpdatePlugin
do opcji strategii po stronie pracownika:
import {registerRoute} from 'workbox-routing';
import {StaleWhileRevalidate} from 'workbox-strategies';
import {BroadcastUpdatePlugin} from 'workbox-broadcast-update';
registerRoute(
({url}) => url.pathname.startsWith('/api/'),
new StaleWhileRevalidate({
plugins: [
new BroadcastUpdatePlugin(),
],
})
);
W aplikacji internetowej możesz nasłuchiwać tych zdarzeń w ten sposób:
navigator.serviceWorker.addEventListener('message', async (event) => {
// Optional: ensure the message came from workbox-broadcast-update
if (event.data.meta === 'workbox-broadcast-update') {
const {cacheName, updatedUrl} = event.data.payload;
// Do something with cacheName and updatedUrl.
// For example, get the cached content and update
// the content on the page.
const cache = await caches.open(cacheName);
const updatedResponse = await cache.match(updatedUrl);
const updatedText = await updatedResponse.text();
}
});
Korzystanie z interfejsów API przeglądarek
Jeśli funkcje Workbox nie wystarczają do Twoich potrzeb, użyj tych interfejsów API przeglądarki, aby zaimplementować „aktualizacje rozsyłane”:
Interfejs Broadcast Channel API
Skrypt service worker tworzy obiekt BroadcastChannel i zaczyna wysyłać do niego wiadomości. Każdy kontekst (np. strona) zainteresowany odbieraniem tych wiadomości może utworzyć instancję obiektu BroadcastChannel
i zaimplementować w nim element obsługi wiadomości.
Aby poinformować stronę o zainstalowaniu nowego skryptu service worker, użyj tego kodu:
// Create Broadcast Channel to send messages to the page
const broadcast = new BroadcastChannel('sw-update-channel');
self.addEventListener('install', function (event) {
// Inform the page every time a new service worker is installed
broadcast.postMessage({type: 'CRITICAL_SW_UPDATE'});
});
Strona nasłuchuje tych zdarzeń, subskrybując te zdarzenia:
// Create Broadcast Channel and listen to messages sent to it
const broadcast = new BroadcastChannel('sw-update-channel');
broadcast.onmessage = (event) => {
if (event.data && event.data.type === 'CRITICAL_SW_UPDATE') {
// Show "update to refresh" banner to the user.
}
};
To prosta metoda, ale jej ograniczeniem jest obsługa przez przeglądarki: w momencie pisania tego artykułu Safari nie obsługuje tego interfejsu API.
Client API
Interfejs API klienta zapewnia prosty sposób komunikowania się z wieloma klientami z poziomu service workera przez iterowanie w tablicy obiektów Client
.
Aby wysłać wiadomość do ostatniej aktywnej karty, użyj tego kodu usługi:
// Obtain an array of Window client objects
self.clients.matchAll(options).then(function (clients) {
if (clients && clients.length) {
// Respond to last focused tab
clients[0].postMessage({type: 'MSG_ID'});
}
});
Strona implementuje moduł obsługi wiadomości, aby przechwytywać te wiadomości:
// Listen to messages
navigator.serviceWorker.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
// Process response
}
};
Interfejs Client API doskonale nadaje się do przesyłania informacji na wiele aktywnych kart. Interfejs API jest obsługiwany przez wszystkie popularne przeglądarki, ale nie wszystkie dostępne metody tak. Przed użyciem sprawdź, czy Twoja przeglądarka obsługuje tę funkcję.
Kanał wiadomości
Kanał wiadomości wymaga wstępnej konfiguracji polegającej na przekazaniu portu ze strony do usługi internetowej, aby ustanowić kanał komunikacji między nimi. Strona tworzy instancję obiektu MessageChannel
i przekazuje skryptowi service worker interfejs postMessage()
:
const messageChannel = new MessageChannel();
// Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
messageChannel.port2,
]);
Strona nasłuchuje wiadomości, implementując na tym porcie uchwyt za pomocą metody „onmessage”:
// Listen to messages
messageChannel.port1.onmessage = (event) => {
// Process message
};
Skrypt service worker odbiera port i zapisuje do niego odwołanie:
// Initialize
let communicationPort;
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PORT_INITIALIZATION') {
communicationPort = event.ports[0];
}
});
Od tego momentu może wysyłać wiadomości do strony, wywołując postMessage()
w odniesieniu do portu:
// Communicate
communicationPort.postMessage({type: 'MSG_ID' });
MessageChannel
może być trudniejszy do wdrożenia ze względu na konieczność inicjowania portów, ale jest obsługiwany przez wszystkie główne przeglądarki.
Dalsze kroki
W tym przewodniku omówiliśmy jeden konkretny przypadek komunikacji z użyciem okna dla usług: „aktualizacje w ramach transmisji”. W przykładach omawianych w tym artykule uwzględniono m.in. słuchanie ważnych zdarzeń cyklu życia usługi oraz komunikowanie się z stroną o zmianach w treści lub danych w pamięci podręcznej. Możesz wymyślić bardziej interesujące przypadki użycia, w których usługa robocza komunikuje się z witryną w sposób proaktywny, bez wcześniejszego otrzymywania wiadomości.
Więcej wzorów komunikacji między oknem a usługą znajdziesz w tych artykułach:
- Przewodnik po pamięci podręcznej imperatywnej: wywołanie skryptu service worker ze strony w celu wcześniejszego zapisania zasobów w pamięci podręcznej (np. w scenariuszach wstępnego pobierania).
- Komunikacja dwukierunkowa: delegowanie zadania do service workera (np. pobieranie dużych plików) i informowanie strony o postępach.