In alcuni scenari, il service worker potrebbe dover comunicare in modo proattivo con una delle schede attive che controlla per informare di un determinato evento. Ecco alcuni esempi:
- Informare la pagina quando è stata installata una nuova versione del service worker, in modo che la pagina possa mostrare all'utente un pulsante "Aggiorna per ricaricare" per accedere immediatamente alla nuova funzionalità.
- Informare l'utente di una modifica dei dati memorizzati nella cache avvenuta sul lato del service worker, mediante un'indicazione, ad esempio: "L'app è ora pronta per funzionare offline" o "Nuova versione dei contenuti disponibile".
Chiameremo questi tipi di casi d'uso in cui il service worker non deve ricevere un messaggio da la pagina per avviare una comunicazione "aggiornamenti di trasmissione". In questa guida esamineremo diversi modi per implementare questo tipo di comunicazione tra pagine e service worker, utilizzando le API standard del browser e la libreria Workbox.
Casi di produzione
Tinder
La PWA di Tinder utilizza workbox-window per ascoltare i
momenti importanti del ciclo di vita del service worker dalla pagina ("installed", "controlled" e
"activated"). In questo modo, quando entra in gioco un nuovo service worker, viene visualizzato un "Aggiornamento disponibile"
banner, in modo che gli utenti possano aggiornare la PWA e accedere alle funzionalità più recenti:
Squoosh
Nella PWA di Squoosh, quando il service worker ha memorizzato nella cache tutte le risorse necessarie per il funzionamento offline, invia un messaggio alla pagina per mostrare una notifica toast "Pronto per funzionare offline" , informando l'utente della funzionalità:
Utilizzo di Workbox
Ascoltare gli eventi del ciclo di vita del service worker
workbox-window fornisce un'interfaccia semplice per ascoltare gli eventi importanti del ciclo di vita del service worker.
Dietro le quinte, la libreria utilizza API lato client come
updatefound
e statechange
e fornisce listener di eventi di livello superiore nell'oggetto workbox-window, semplificando l'utilizzo di questi eventi da parte dell'
utente.
Il seguente codice della pagina consente di rilevare ogni volta che viene installata una nuova versione del service worker, in modo da poterla comunicare all'utente:
const wb = new Workbox('/sw.js');
wb.addEventListener('installed', (event) => {
if (event.isUpdate) {
// Show "Update App" banner
}
});
wb.register();
Informare la pagina delle modifiche ai dati della cache
Il pacchetto Workbox
workbox-broadcast-update
fornisce un modo standard per notificare ai client della finestra che una risposta memorizzata nella cache è stata aggiornata. Questo viene
utilizzato più comunemente insieme alla strategia StaleWhileRevalidate.
Per trasmettere gli aggiornamenti, aggiungi un broadcastUpdate.BroadcastUpdatePlugin alle opzioni della strategia sul lato del 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(),
],
})
);
Nella tua app web, puoi ascoltare questi eventi nel seguente modo:
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();
}
});
Utilizzo delle API del browser
Se la funzionalità fornita da Workbox non è sufficiente per le tue esigenze, utilizza le seguenti API del browser per implementare "aggiornamenti di trasmissione":
API Broadcast Channel
Il service worker crea un oggetto BroadcastChannel
object e inizia a inviargli
messaggi. Qualsiasi contesto (ad es. pagina) interessato a ricevere questi messaggi può creare un'istanza di un oggetto BroadcastChannel e implementare un gestore di messaggi per ricevere i messaggi.
Per informare la pagina quando viene installato un nuovo service worker, utilizza il seguente codice:
// 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'});
});
La pagina ascolta questi eventi sottoscrivendo 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.
}
};
Si tratta di una tecnica semplice, ma la sua limitazione è il supporto del browser: al momento della stesura di questo documento, Safari non supporta questa API.
API Client
L'API Client fornisce un modo semplice
per comunicare con più client dal service worker scorrendo un array di
Client oggetti.
Utilizza il seguente codice del service worker per inviare un messaggio all'ultima scheda attiva:
// 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'});
}
});
La pagina implementa un gestore di messaggi per intercettare questi messaggi:
// Listen to messages
navigator.serviceWorker.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
// Process response
}
};
L'API Client è un'ottima opzione per casi come la trasmissione di informazioni a più schede attive. L'API è supportata da tutti i principali browser, ma non tutti i suoi metodi. Verifica il supporto del browser prima di utilizzarla.
Canale messaggi
Il canale messaggi richiede
un passaggio di configurazione iniziale, passando una porta dalla pagina al service worker, per stabilire un
canale di comunicazione tra loro. La pagina crea un'istanza di un oggetto MessageChannel e passa una porta al service worker tramite l'interfaccia postMessage():
const messageChannel = new MessageChannel();
// Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
messageChannel.port2,
]);
La pagina ascolta i messaggi implementando un gestore "onmessage" su quella porta:
// Listen to messages
messageChannel.port1.onmessage = (event) => {
// Process message
};
Il service worker riceve la porta e ne salva un riferimento:
// Initialize
let communicationPort;
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PORT_INITIALIZATION') {
communicationPort = event.ports[0];
}
});
Da questo punto in poi, può inviare messaggi alla pagina chiamando postMessage() nel riferimento alla porta:
// Communicate
communicationPort.postMessage({type: 'MSG_ID' });
MessageChannel potrebbe essere più complesso da implementare, a causa della necessità di inizializzare le porte, ma è
supportato da tutti i principali browser.
Passaggi successivi
In questa guida abbiamo esplorato un caso particolare di comunicazione da finestra a service worker: "aggiornamenti di trasmissione". Gli esempi esplorati includono l'ascolto di eventi importanti del ciclo di vita del service worker e la comunicazione alla pagina delle modifiche ai contenuti o ai dati memorizzati nella cache. Puoi pensare a casi d'uso più interessanti in cui il service worker comunica in modo proattivo con la pagina, senza ricevere alcun messaggio in precedenza.
Per altri pattern di comunicazione tra finestra e service worker, consulta:
- Guida alla memorizzazione nella cache imperativa: chiamare un service worker dalla pagina per memorizzare le risorse nella cache in anticipo (ad es. negli scenari di prefetching).
- Comunicazione bidirezionale: delegare un'attività a un service worker (ad es. un download pesante) e tenere informata la pagina sull'avanzamento.