Trasmissione di aggiornamenti alle pagine con i service worker

Andrew Guan
Andrew Guan
Demián Renzulli
Demián Renzulli

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".
Diagramma che mostra un service worker che comunica con la pagina per inviare un aggiornamento.

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:

Uno screenshot della funzionalità "Aggiornamento disponibile" della web app di Tinder.
Nella PWA di Tinder, il service worker comunica alla pagina che è pronta una nuova versione e la pagina mostra agli utenti un banner "Aggiornamento disponibile".

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à:

Screenshot della funzionalità "Pronto per l'utilizzo offline" dell'app web Squoosh.
Nella PWA di Squoosh, il service worker trasmette un aggiornamento alla pagina quando la cache è pronta e la pagina visualizza la notifica toast "Pronto per funzionare offline".

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:

Risorse aggiuntive