Em alguns casos, um app da Web pode precisar estabelecer um canal de comunicação bidirecional entre as e o service worker.
Por exemplo: em um PWA de podcast, é possível criar um recurso para permitir que o usuário faça o download de episódios do consumo off-line e permitir que service worker para manter a página regularmente informada sobre o progresso, de modo que os recursos linha de execução podem atualizar a interface.
Neste guia, exploraremos as diferentes maneiras de implementar uma comunicação bidirecional entre as opções Window e service profissional, analisando diferentes APIs, a biblioteca Workbox, bem como alguns casos avançados.
Como usar o Workbox
workbox-window
é um conjunto de
da biblioteca Workbox que se destinam
seja executado no contexto da janela. O Workbox
A classe fornece um método messageSW()
para enviar uma mensagem ao service worker registrado da instância e
aguardam uma resposta.
O código da página a seguir cria uma nova instância Workbox
e envia uma mensagem para o service worker
para obter sua versão:
const wb = new Workbox('/sw.js');
wb.register();
const swVersion = await wb.messageSW({type: 'GET_VERSION'});
console.log('Service Worker version:', swVersion);
O service worker implementa um listener de mensagens na outra extremidade e responde ao service worker:
const SW_VERSION = '1.0.0';
self.addEventListener('message', (event) => {
if (event.data.type === 'GET_VERSION') {
event.ports[0].postMessage(SW_VERSION);
}
});
Internamente, a biblioteca usa uma API de navegador que analisaremos na próxima seção: Mensagem canal, mas abstrai muitos detalhes de implementação, facilitando o uso e aproveitando a amplitude do navegador suporte dessa API.
Como usar as APIs do navegador
Se a biblioteca Workbox não for suficiente para suas necessidades, há várias APIs de nível inferior disponíveis para implementar a comunicação bidirecional entre páginas e service workers. Eles têm algumas semelhanças e diferenças:
Semelhanças:
- Em todos os casos, a comunicação começa em uma extremidade pela interface
postMessage()
e é recebida por outro lado, implementando um gerenciadormessage
. - Na prática, todas as APIs disponíveis nos permitem implementar os mesmos casos de uso, mas algumas delas pode simplificar o desenvolvimento em alguns cenários.
Diferenças:
- Eles têm maneiras diferentes de identificar o outro lado da comunicação: alguns usam um referência explícita ao outro contexto, enquanto outros podem se comunicar implicitamente por meio de um proxy objeto instanciado em cada lado.
- O suporte ao navegador varia entre eles.
API Broadcast Channel
A API Broadcast Channel permite a comunicação básica entre contextos de navegação via BroadcastChannel objetos.
Para implementá-lo, primeiro, cada contexto precisa instanciar um objeto BroadcastChannel
com o mesmo ID
e enviar e receber mensagens dele:
const broadcast = new BroadcastChannel('channel-123');
O objeto BroadcastChannel expõe uma interface postMessage()
para enviar uma mensagem a qualquer pessoa que ouve
contexto:
//send message
broadcast.postMessage({ type: 'MSG_ID', });
Qualquer contexto de navegador pode ouvir mensagens pelo método onmessage
da BroadcastChannel
objeto:
//listen to messages
broadcast.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//process message...
}
};
Como visto, não há referência explícita a um contexto específico, portanto, não é necessário obter um referência primeiro ao service worker ou a qualquer cliente em particular.
A desvantagem é que, no momento em que este artigo foi escrito, a API é compatível com Chrome, Firefox e o Edge, mas outros navegadores, como o Safari, não são compatíveis com esse recurso. ainda.
API do cliente
Com a API do cliente, é possível
referência a todos os objetos WindowClient
que representam as guias ativas que o service worker está controlando.
Como a página é controlada por um único service worker, ela detecta e envia mensagens para o
service worker ativo diretamente por meio da interface serviceWorker
:
//send message
navigator.serviceWorker.controller.postMessage({
type: 'MSG_ID',
});
//listen to messages
navigator.serviceWorker.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//process response
}
};
Da mesma forma, o service worker detecta mensagens implementando um listener onmessage
:
//listen to messages
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//Process message
}
});
Para se comunicar de volta com qualquer um de seus clientes, o service worker obtém uma matriz de
Objetos WindowClient
executando
métodos como
Clients.matchAll()
e
Clients.get()
. Assim, é possível
postMessage()
qualquer um deles:
//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'});
}
});
O Client API
é uma boa opção para se comunicar facilmente com todas as guias ativas de um service worker
de forma relativamente simples. A API é compatível com todas as principais
navegadores da Web,
mas talvez nem todos os métodos estejam disponíveis. Portanto, verifique o suporte do navegador antes
implementá-las em seu site.
Canal de mensagens
O canal de mensagem exige definir e transmitir uma porta de um contexto a outro para estabelecer uma comunicação bidirecional. canal.
Para inicializar o canal, a página instancia e usa um objeto MessageChannel
para enviar uma porta ao service worker registrado. A página também implementa um listener onmessage
em
para receber mensagens do outro contexto:
const messageChannel = new MessageChannel();
//Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
messageChannel.port2,
]);
//Listen to messages
messageChannel.port1.onmessage = (event) => {
// Process message
};
O service worker recebe a porta, salva uma referência a ela e a usa para enviar uma mensagem para o outro lado:
let communicationPort;
//Save reference to port
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PORT_INITIALIZATION') {
communicationPort = event.ports[0];
}
});
//Send messages
communicationPort.postMessage({type: 'MSG_ID'});
Atualmente, o MessageChannel
é suportado por todas as principais
navegadores da Web.
APIs avançadas: sincronização e busca em segundo plano
Neste guia, exploramos maneiras de implementar técnicas de comunicação bidirecional, para relativamente casos simples, como passar uma mensagem de string descrevendo a operação a ser executada ou uma lista de URLs armazenar em cache de um contexto para outro. Nesta seção, vamos conhecer duas APIs para lidar com para estes cenários: falta de conectividade e downloads longos.
Sincronização em segundo plano
Um app de chat pode querer garantir que as mensagens nunca sejam perdidas devido a uma conectividade ruim. O A API Background Sync permite adiar ações para que sejam repetidas quando o usuário tiver conectividade estável. Isso é útil para garantir que tudo o que o usuário quer enviar é de fato enviado.
Em vez da interface postMessage()
, a página registra um sync
:
navigator.serviceWorker.ready.then(function (swRegistration) {
return swRegistration.sync.register('myFirstSync');
});
Em seguida, o service worker detecta o evento sync
para processar a mensagem:
self.addEventListener('sync', function (event) {
if (event.tag == 'myFirstSync') {
event.waitUntil(doSomeStuff());
}
});
A função doSomeStuff()
precisa retornar uma promessa indicando o sucesso/fracasso de tudo o que
tentando fazer. Em caso afirmativo, a sincronização estará concluída. Se ela falhar, outra sincronização será agendada para
tentar de novo. Novas sincronizações também aguardam a conectividade e empregam uma espera exponencial.
Depois que a operação for executada, o service worker poderá se comunicar de volta com a página para atualizar a IU usando qualquer uma das APIs de comunicação exploradas anteriormente.
A Pesquisa Google usa a sincronização em segundo plano para manter consultas com falha devido a conectividade ruim e tente novamente. mais tarde, quando o usuário estiver on-line. Depois que a operação é realizada, eles comunicam o resultado para o usuário por meio de uma notificação push da Web:
Busca em segundo plano
Para trabalhos relativamente curtos, como enviar uma mensagem ou uma lista de URLs para armazenar em cache, as opções exploradas até agora são uma boa escolha. Se a tarefa demorar muito, o navegador encerrará o serviço do dispositivo, porque isso seria um risco para a privacidade e a bateria do usuário.
A API Background Fetch permite que você descarregue uma tarefa longa para um service worker, como o download de filmes, podcasts ou níveis de um jogo.
Para se comunicar com o service worker pela página, use backgroundFetch.fetch
, em vez de
postMessage()
:
navigator.serviceWorker.ready.then(async (swReg) => {
const bgFetch = await swReg.backgroundFetch.fetch(
'my-fetch',
['/ep-5.mp3', 'ep-5-artwork.jpg'],
{
title: 'Episode 5: Interesting things.',
icons: [
{
sizes: '300x300',
src: '/ep-5-icon.png',
type: 'image/png',
},
],
downloadTotal: 60 * 1024 * 1024,
},
);
});
O objeto BackgroundFetchRegistration
permite que a página detecte o evento progress
para seguir
o andamento do download:
bgFetch.addEventListener('progress', () => {
// If we didn't provide a total, we can't provide a %.
if (!bgFetch.downloadTotal) return;
const percent = Math.round(
(bgFetch.downloaded / bgFetch.downloadTotal) * 100,
);
console.log(`Download progress: ${percent}%`);
});
Próximas etapas
Neste guia, exploramos o caso mais geral de comunicação entre os page workers e os service workers. (comunicação bidirecional).
Muitas vezes, pode ser necessário apenas um contexto para se comunicar com o outro, sem receber uma resposta. Confira os guias a seguir para saber como implementar técnicas unidirecionais em suas páginas de e para o service worker, junto com casos de uso e exemplos de produção:
- 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);
- Transmitir atualizações: chamar a página do service worker para informar sobre atualizações importantes (por exemplo, uma nova versão do webapp está disponível).