Transmitir atualizações para páginas com os service workers

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

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".
Diagrama mostrando um service worker se comunicando com a página para enviar uma atualização.

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:

Uma captura de tela da "Atualização disponível" do webapp do Tinder funcionalidade de armazenamento.
No PWA do Tinder, o service worker informa à página que uma nova versão está pronta e mostra aos usuários uma mensagem "UpdateAvailable" banner.

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:

Uma captura de tela do webapp da Squoosh "Pronto para trabalhar off-line" funcionalidade de armazenamento.
No PWA do Squoosh, o service worker transmite uma atualização para a página quando o cache está pronto, e a página exibe "Pronto para trabalhar off-line" torcer.

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:

Outros recursos