W niektórych przypadkach service worker może potrzebować aktywnie komunikować się z dowolną z aktywnych kart, które kontroluje, aby poinformować o określonym zdarzeniu. Przykłady:
- Informowanie strony o zainstalowaniu nowej wersji skryptu service worker, aby mogła wyświetlać użytkownikowi przycisk „Odśwież, aby zaktualizować”, który umożliwia natychmiastowe korzystanie z nowych funkcji.
- Informowanie użytkownika o zmianie w danych przechowywanych w pamięci podręcznej, która nastąpiła po stronie skryptu service worker, poprzez wyświetlanie informacji, np. „Aplikacja jest teraz gotowa do pracy w trybie offline” lub „Dostępna jest nowa wersja treści”.
Takie przypadki użycia, w których skrypt service worker nie musi otrzymywać wiadomości ze strony, aby rozpocząć komunikację, będziemy nazywać „aktualizacjami rozgłoszeniowymi”. W tym przewodniku omówimy różne sposoby implementowania tego typu komunikacji między stronami a procesami service worker przy użyciu standardowych interfejsów API przeglądarki i biblioteki Workbox.
Przypadki produkcyjne
Tinder
Aplikacja PWA Tinder korzysta z workbox-window, aby nasłuchiwać ważnych momentów cyklu życia skryptu service worker na stronie („zainstalowany”, „kontrolowany” i „aktywowany”). Dzięki temu, gdy pojawi się nowy skrypt service worker, wyświetli się baner „Dostępna aktualizacja”, aby użytkownik mógł odświeżyć aplikację PWA i uzyskać dostęp do najnowszych funkcji:
Squoosh
W Squoosh PWA, gdy skrypt service worker zapisze w pamięci podręcznej wszystkie niezbędne zasoby, aby aplikacja działała w trybie offline, wysyła do strony komunikat z informacją o gotowości do pracy w trybie offline. Dzięki temu użytkownik dowiaduje się o tej funkcji:
Korzystanie z Workbox
Nasłuchiwanie zdarzeń cyklu życia skryptu service worker
workbox-window udostępnia prosty interfejs do słuchania ważnych zdarzeń związanych z cyklem życia skryptu service worker.
Biblioteka korzysta z interfejsów API po stronie klienta, takich jak updatefound i statechange, oraz udostępnia słuchaczy zdarzeń wyższego poziomu w obiekcie workbox-window, co ułatwia użytkownikowi korzystanie z tych zdarzeń.
Poniższy kod strony umożliwia wykrywanie każdej instalacji nowej wersji skryptu service worker, dzięki czemu możesz informować o tym użytkownika:
const wb = new Workbox('/sw.js');
wb.addEventListener('installed', (event) => {
if (event.isUpdate) {
// Show "Update App" banner
}
});
wb.register();
Informowanie strony o zmianach w danych z pamięci podręcznej
Pakiet Workbox
workbox-broadcast-update
zapewnia standardowy sposób powiadamiania klientów okien o zaktualizowaniu odpowiedzi w pamięci podręcznej. Jest to najczęściej używane w połączeniu ze strategią StaleWhileRevalidate.
Aby transmitować aktualizacje, dodaj broadcastUpdate.BroadcastUpdatePlugin do opcji strategii po stronie skryptu service worker:
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ądarki
Jeśli funkcje udostępniane przez Workbox nie są wystarczające, użyj tych interfejsów API przeglądarki, aby zaimplementować „aktualizacje rozgłoszeniowe”:
Broadcast Channel API
Skrypt service worker tworzy obiekt BroadcastChannel i zaczyna wysyłać do niego wiadomości. Każdy kontekst (np. strona), który jest zainteresowany otrzymywaniem tych wiadomości, może utworzyć instancję obiektu BroadcastChannel i wdrożyć procedurę obsługi wiadomości, aby je odbierać.
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 sw-update-channel:
// 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.
}
};
Jest to prosta metoda, ale ma ograniczenie w postaci obsługi przez przeglądarki: w momencie pisania tego artykułu Safari nie obsługuje tego interfejsu API.
Client API
Interfejs Client API zapewnia prosty sposób komunikacji z wieloma klientami z poziomu skryptu service worker przez iterację po tablicy obiektów Client.
Aby wysłać wiadomość do ostatniej aktywnej karty, użyj tego kodu skryptu service worker:
// 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, który przechwytuje te wiadomości:
// Listen to messages
navigator.serviceWorker.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
// Process response
}
};
Interfejs Client API to świetne rozwiązanie w przypadku takich działań jak przesyłanie informacji do wielu aktywnych kart. Interfejs API jest obsługiwany przez wszystkie główne przeglądarki, ale nie wszystkie jego metody. Zanim zaczniesz z niej korzystać, sprawdź, czy jest obsługiwana przez Twoją przeglądarkę.
Kanał wiadomości
Message Channel wymaga początkowego kroku konfiguracji, polegającego na przekazaniu portu ze strony do skryptu service worker, aby utworzyć między nimi kanał komunikacji. Strona tworzy obiekt MessageChannel i przekazuje port do skryptu service worker za pomocą interfejsu 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 procedurę obsługi „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 między oknem a skryptem service worker: „przesyłanie aktualizacji”. Przykłady obejmują nasłuchiwanie ważnych zdarzeń cyklu życia skryptów service worker i informowanie strony o zmianach treści lub danych przechowywanych w pamięci podręcznej. Możesz wymyślić ciekawsze przypadki użycia, w których service worker aktywnie komunikuje się ze stroną bez wcześniejszego otrzymania żadnej wiadomości.
Więcej wzorców komunikacji między oknem a skryptem service worker znajdziesz w tych materiałach:
- Przewodnik po buforowaniu imperatywnym: wywoływanie skryptu service worker ze strony w celu wcześniejszego buforowania zasobów (np. w scenariuszach wstępnego pobierania).
- Komunikacja dwukierunkowa: delegowanie zadania do komponentu service worker (np. pobieranie dużego pliku) i informowanie strony o postępach.