Em alguns cenários, o service worker pode precisar se comunicar proativamente com qualquer um dos guias 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 Mostrar um botão "Atualizar para atualizar" ao usuário para acessar a nova funcionalidade imediatamente.
- Informar o usuário sobre uma alteração nos dados armazenados em cache que ocorreu no lado do service worker ao mostrando uma indicação, como: "O app está pronto para funcionar off-line" ou "Nova versão do conteúdo disponível".
Chamaremos esses tipos de casos de uso em que o service worker não precisa receber uma mensagem página para iniciar uma comunicação "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 padrões APIs do navegador e a biblioteca Workbox.
Casos de produção
Tinder
O PWA do Tinder 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 jogo, ele vai mostrar uma mensagem "Atualização disponível".
para que possam atualizar o PWA e acessar os recursos mais recentes:
Azul-celeste
No Squoosh PWA, quando o service worker armazena em cache todos os para fazê-lo funcionar off-line, ele envia uma mensagem para a página com a mensagem "Pronto para trabalhar off-line" informando ao usuário sobre o recurso:
Como usar o Workbox
Detectar eventos de ciclo de vida do service worker
workbox-window
fornece uma interface simples para detectar ciclo de vida importante do service worker
eventos.
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
para consumir esses eventos.
O código da página a seguir permite detectar sempre que uma nova versão do service worker é instalada. para que você possa comunicá-lo 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 mudanças nos dados em cache
O pacote Workbox
workbox-broadcast-update
fornece uma maneira padrão de notificar os clientes da janela de que uma resposta armazenada em cache foi atualizada. Isso é
mais usada, juntamente com a solicitação StalewhileRevalidate
estratégia.
Para transmitir atualizações, adicione uma broadcastUpdate.BroadcastUpdatePlugin
às opções da sua estratégia no
lado do 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, você pode detectar esses eventos da seguinte forma:
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 as APIs do navegador
Se a funcionalidade do Workbox não for suficiente para você, use o seguinte navegador APIs para implementar atualizações de transmissão:
API Broadcast Channel
O service worker cria um objeto BroadcastChannel
objeto e começa a enviar
mensagens para ele. Qualquer contexto (por exemplo, página) interessado em receber essas mensagens pode instanciar uma
BroadcastChannel
e implemente 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 inscrevendo-se no 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.
}
};
Essa é uma técnica simples, mas sua limitação é o suporte ao navegador: no momento em que este artigo foi escrito, O Safari não é compatível com essa API.
API do cliente
A API do cliente fornece uma
forma de se comunicar com vários clientes do service worker, iterando uma matriz de
Client
.
Use o seguinte código do service worker 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 de mensagens para interceptar essas mensagens:
// Listen to messages
navigator.serviceWorker.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
// Process response
}
};
A API Client é uma ótima opção para casos como a transmissão de informações para várias guias ativas. A A API é suportada por todos os principais navegadores, mas nem todos os seus métodos são. Verifique a compatibilidade com o navegador antes usá-lo.
Canal de mensagens
O canal de mensagem exige
uma etapa de configuração inicial, passando uma porta da página para o service worker, para estabelecer uma
canal de comunicação entre elas. A página instancia um objeto MessageChannel
e transmite um
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 as mensagens implementando um "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 ao método
porta:
// Communicate
communicationPort.postMessage({type: 'MSG_ID' });
A implementação do MessageChannel
pode ser mais complexa devido à necessidade de inicializar portas, mas é
compatível com todos os principais navegadores.
Próximas etapas
Neste guia, exploramos um caso específico da comunicação da janela para o service worker: "atualizações de transmissão". Entre os exemplos analisados estão a detecção de importantes service workers eventos de ciclo de vida e informar a página sobre mudanças no conteúdo ou nos dados armazenados em cache. Você pode pensar mais casos de uso interessantes em que o service worker se comunica proativamente com a página, sem ter recebido nenhuma mensagem antes.
Para mais padrões de comunicação da janela e do service worker, confira:
- Guia de armazenamento em cache imperativo: 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.