En algunos casos, es posible que el service worker deba comunicarse de forma proactiva con cualquiera de las pestañas activas que controla para informar sobre un evento determinado. Los siguientes son algunos ejemplos:
- Informa a la página cuando se instala una nueva versión del Service Worker para que la página pueda mostrarle al usuario un botón de "Actualizar para actualizar" y acceder a la nueva funcionalidad de inmediato.
- Informar al usuario sobre un cambio en los datos almacenados en caché que se produjo en el service worker, mostrando una indicación, como "La app ahora está lista para funcionar sin conexión" o "Hay una nueva versión del contenido disponible"
Llamaremos a estos tipos de casos de uso en los que el service worker no necesita recibir un mensaje de la página para iniciar una comunicación "actualizaciones de transmisión". En esta guía, revisaremos diferentes formas de implementar este tipo de comunicación entre páginas y service workers con las APIs estándar del navegador y la biblioteca de Workbox.
Casos de producción
Tinder
La PWA de Tinder usa workbox-window para escuchar momentos importantes del ciclo de vida del service worker desde la página ("instalado", "controlado" y "activado"). De esa manera, cuando entra en juego un nuevo Service Worker, se muestra un banner de "Actualización disponible" para que puedan actualizar la PWA y acceder a las funciones más recientes:
Squoosh
En la PWA de Squoosh, cuando el service worker ha almacenado en caché todos los recursos necesarios para que funcione sin conexión, envía un mensaje a la página para mostrar un aviso "Listo para trabajar sin conexión", lo que le permite al usuario conocer la función:
Cómo usar Workbox
Cómo detectar eventos de ciclo de vida del service worker
workbox-window proporciona una interfaz sencilla para escuchar eventos importantes del ciclo de vida del service worker.
En segundo plano, la biblioteca usa APIs del cliente, como updatefound y statechange, y proporciona objetos de escucha de eventos de nivel superior en el objeto workbox-window, lo que facilita al usuario el consumo de estos eventos.
El siguiente código de página te permite detectar cada vez que se instala una nueva versión del service worker, de modo que puedas comunicárselo al usuario:
const wb = new Workbox('/sw.js');
wb.addEventListener('installed', (event) => {
if (event.isUpdate) {
// Show "Update App" banner
}
});
wb.register();
Informa a la página sobre los cambios en los datos de caché
El paquete Workbox workbox-broadcast-update proporciona una forma estándar de notificar a los clientes de la ventana que se actualizó una respuesta almacenada en caché. Por lo general, se usa junto con la estrategia StaleWhileRevalidate.
Para transmitir actualizaciones, agrega un broadcastUpdate.BroadcastUpdatePlugin a las opciones de tu estrategia en el 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(),
],
})
);
En tu app web, puedes escuchar estos eventos de la siguiente manera:
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();
}
});
Cómo usar las APIs del navegador
Si la funcionalidad que proporciona Workbox no es suficiente para tus necesidades, usa las siguientes APIs del navegador para implementar "actualizaciones de transmisión":
API de Broadcast Channel
El service worker crea un objeto BroadcastChannel y comienza a enviarle mensajes. Cualquier contexto (p.ej., una página) interesado en recibir estos mensajes puede crear una instancia de un objeto BroadcastChannel y, luego, implementar un controlador de mensajes para recibirlos.
Para informar a la página cuando se instala un nuevo Service Worker, usa el siguiente código:
// 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 página escucha estos eventos suscribiéndose a 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.
}
};
Esta es una técnica simple, pero su limitación es la compatibilidad con el navegador: al momento de escribir este artículo, Safari no admite esta API.
API del cliente
La API de Client proporciona una forma sencilla de comunicarse con varios clientes desde el service worker a través de la iteración en un array de objetos Client.
Usa el siguiente código del trabajador de servicio para enviar un mensaje a la última pestaña enfocada:
// 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 página implementa un controlador de mensajes para interceptar estos mensajes:
// Listen to messages
navigator.serviceWorker.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
// Process response
}
};
La API de Client es una excelente opción para casos como la transmisión de información a varias pestañas activas. Todos los navegadores principales admiten la API, pero no todos sus métodos. Verifica la compatibilidad del navegador antes de usarlo.
Canal de mensajes
Message Channel requiere un paso de configuración inicial, que consiste en pasar un puerto de la página al service worker, para establecer un canal de comunicación entre ellos. La página crea una instancia de un objeto MessageChannel y pasa un puerto al service worker a través de la interfaz postMessage():
const messageChannel = new MessageChannel();
// Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
messageChannel.port2,
]);
La página escucha los mensajes implementando un controlador "onmessage" en ese puerto:
// Listen to messages
messageChannel.port1.onmessage = (event) => {
// Process message
};
El trabajador de servicio recibe el puerto y guarda una referencia a él:
// Initialize
let communicationPort;
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PORT_INITIALIZATION') {
communicationPort = event.ports[0];
}
});
A partir de ese momento, puede enviar mensajes a la página llamando a postMessage() en la referencia al puerto:
// Communicate
communicationPort.postMessage({type: 'MSG_ID' });
MessageChannel podría ser más complejo de implementar debido a la necesidad de inicializar puertos, pero es compatible con todos los navegadores principales.
Próximos pasos
En esta guía, exploramos un caso particular de comunicación entre la ventana y el service worker: "actualizaciones de transmisión". Los ejemplos explorados incluyen la escucha de eventos importantes del ciclo de vida del Service Worker y la comunicación con la página sobre los cambios en el contenido o los datos almacenados en caché. Puedes pensar en casos de uso más interesantes en los que el service worker se comunica de forma proactiva con la página, sin recibir ningún mensaje previamente.
Para obtener más patrones de comunicación entre la ventana y el service worker, consulta lo siguiente:
- Guía de almacenamiento en caché imperativo: Llama a un service worker desde la página para almacenar en caché los recursos con anticipación (p.ej., en situaciones de recuperación previa).
- Comunicación bidireccional: Delegar una tarea a un service worker (p. ej., una descarga pesada) y mantener la página informada sobre el progreso