Em alguns cenários, o service worker pode precisar se comunicar proativamente com qualquer uma das guias ativas que ele controla para informar sobre um determinado evento. Por exemplo:
- Informar a página quando uma nova versão do service worker tiver sido instalada, para que a página possa mostrar um botão "Atualizar para atualizar" ao usuário e acessar a nova funcionalidade imediatamente.
- Informar o usuário sobre uma mudança nos dados em cache que ocorreu no lado do service worker, mostrando uma indicação, como: "O app agora está pronto para trabalhar off-line" ou "Nova versão do conteúdo disponível".
Vamos chamar esses tipos de casos de uso em que o service worker não precisa receber uma mensagem da página para iniciar uma comunicação de "atualizações de transmissão". Neste guia, vamos analisar diferentes maneiras de implementar esse tipo de comunicação entre páginas e service workers usando APIs de navegador padrão e a biblioteca Workbox.
Casos de produção
Tinder
O Tinder PWA usa workbox-window
para detectar
momentos importantes do ciclo de vida do service worker da página ("instalado", "controlado" e
"ativado"). Dessa forma, quando um novo service worker entrar em ação, ele mostrará um banner "Atualização disponível"
para que ele possa atualizar o PWA e acessar os recursos mais recentes:
Squoosh
No Squoosh PWA, quando o service worker armazena em cache todos os recursos necessários para fazê-lo funcionar off-line, ele envia uma mensagem para a página mostrando um aviso "Pronto para trabalhar off-line", informando ao usuário sobre o recurso:
Usar o Workbox
Detectar eventos de ciclo de vida do service worker
workbox-window
fornece uma interface simples para detectar eventos importantes do ciclo de vida do
service worker.
Internamente, a biblioteca usa APIs do lado do cliente, como
updatefound
e statechange
e fornece listeners de eventos de nível superior no objeto workbox-window
, facilitando o
consumo desses eventos pelo usuário.
O código da página a seguir permite detectar todas as vezes que uma nova versão do service worker é instalada, para que você possa comunicá-la ao usuário:
const wb = new Workbox('/sw.js');
wb.addEventListener('installed', (event) => {
if (event.isUpdate) {
// Show "Update App" banner
}
});
wb.register();
Informar a página sobre alterações nos dados em cache
O pacote Workbox
workbox-broadcast-update
oferece uma maneira padrão de notificar os clientes da janela que uma resposta armazenada em cache foi atualizada. Ela é
mais usada com a estratégia StalewhileRevalidate.
Para transmitir atualizações, adicione um broadcastUpdate.BroadcastUpdatePlugin
às suas opções de estratégia no
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(),
],
})
);
No seu app da Web, é possível detectar esses eventos, como:
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();
}
});
Como usar APIs do navegador
Se a funcionalidade oferecida pelo Workbox não for suficiente para suas necessidades, use as seguintes APIs de navegador para implementar "atualizações de transmissão":
API Broadcast Channel
O service worker cria um objeto
BroadcastChannel e começa a enviar
mensagens para ele. Qualquer contexto (por exemplo, página) interessado em receber essas mensagens pode instanciar um objeto
BroadcastChannel
e implementar um gerenciador para receber mensagens.
Para informar a página quando um novo service worker for instalado, use o seguinte 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'});
});
A página detecta esses eventos ao se inscrever em 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 é uma técnica simples, mas a limitação é a compatibilidade com o navegador. No momento, o Safari não é compatível com essa API.
API do cliente
A API Client oferece uma maneira
direta de se comunicar com vários clientes do service worker iterando uma matriz de
objetos Client
.
Use o código do service worker a seguir para enviar uma mensagem para a última guia em foco:
// 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'});
}
});
A página implementa um gerenciador para interceptar estas mensagens:
// Listen to messages
navigator.serviceWorker.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
// Process response
}
};
A API do cliente é uma ótima opção para casos como transmissão de informações para várias guias ativas. A API é compatível com todos os principais navegadores, mas nem todos os métodos dela são. Verifique a compatibilidade com navegadores antes de usá-lo.
Canal de mensagens
O Message Channel requer uma etapa inicial de configuração, passando uma porta da página da página para o service worker, para estabelecer um canal de comunicação entre eles. A página instancia um objeto MessageChannel
e transmite uma
porta para o service worker pela interface postMessage()
:
const messageChannel = new MessageChannel();
// Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
messageChannel.port2,
]);
A página detecta mensagens implementando um gerenciador "onmessage" nessa porta:
// Listen to messages
messageChannel.port1.onmessage = (event) => {
// Process message
};
O service worker recebe a porta e salva uma referência a ela:
// Initialize
let communicationPort;
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PORT_INITIALIZATION') {
communicationPort = event.ports[0];
}
});
A partir desse ponto, ele poderá enviar mensagens para a página chamando postMessage()
na referência à
porta:
// Communicate
communicationPort.postMessage({type: 'MSG_ID' });
O MessageChannel
pode ser mais complexo de implementar, devido à necessidade de inicializar portas, mas tem
suporte de todos os principais navegadores.
Próximas etapas
Neste guia, exploramos um caso específico de comunicação entre janelas e service worker: "atualizações de transmissão". Os exemplos explorados incluem a detecção de eventos importantes do ciclo de vida do service worker e a comunicação com a página sobre alterações no conteúdo ou nos dados armazenados em cache. Pense em casos de uso mais interessantes, em que o service worker se comunica proativamente com a página, sem receber nenhuma mensagem anteriormente.
Para mais padrões de comunicação do Window e do service worker, confira:
- Guia imperativo de armazenamento em cache: chamar um service worker da página para armazenar recursos em cache com antecedência (por exemplo, em cenários de pré-busca).
- Comunicação bidirecional: delegar uma tarefa a um service worker (por exemplo, um download pesado) e manter a página informada sobre o progresso.
Outros recursos
- workbox-window (em inglês)
- workbox-broadcast-update (link em inglês)
- Workbox 4: como implementar o fluxo de atualização para atualização de versão usando o módulo da caixa de trabalho (em inglês)