Alguns sites podem precisar se comunicar com o service worker sem a necessidade informada sobre o resultado. Veja alguns exemplos:
- Uma página envia ao service worker uma lista de URLs para pré-busca, para que, quando o usuário clicar em um que os sub-recursos do documento ou da página já estejam disponíveis no cache, tornando as a navegação com muito mais rapidez.
- A página solicita que o service worker recupere e armazene em cache um conjunto de matérias importantes para que sejam disponíveis para fins off-line.
Delegar esses tipos de tarefas não críticas ao service worker tem o benefício de liberar o linha de execução principal para lidar melhor com tarefas mais urgentes, como responder às interações do usuário.
Neste guia, exploraremos como implementar uma técnica de comunicação unidirecional da página para o service worker usando APIs de navegador padrão e a biblioteca Workbox. Chamaremos esses tipos de casos de uso de armazenamento em cache imperativo.
Caso de produção
O 1-800-Flowers.com implementou o armazenamento em cache imperativo (pré-busca) com service workers via
postMessage()
para pré-buscar o
principais itens nas páginas de categorias para acelerar a navegação subsequente às páginas de detalhes do produto.
A equipe usa uma abordagem mista para decidir quais itens serão pré-buscados:
- No tempo de carregamento da página, ele pede ao servicer worker que recupere os dados JSON dos nove itens principais e adicione os objetos de resposta resultantes ao cache.
- Para os itens restantes, ele ouve o
mouseover
evento, de modo que, quando um usuário mover o cursor sobre um item, poderá acionar uma busca do recurso sob demanda.
Eles usam a API Cache para armazenar dados JSON. respostas:
Quando o usuário clica em um item, os dados JSON associados a ele podem ser extraídos do cache, sem a necessidade de acessar a rede, tornando a navegação mais rápida.
Como usar o Workbox
O Workbox oferece uma maneira fácil de enviar mensagens para
Um service worker, pelo pacote workbox-window
, um conjunto de módulos
que são executados no contexto da janela. Eles são um complemento para os outros pacotes de caixa de trabalho
executados no service worker.
Para comunicar a página com o service worker, primeiro obtenha uma referência do objeto Workbox para o service worker registrado:
const wb = new Workbox('/sw.js');
wb.register();
Então, você pode enviar a mensagem diretamente de forma declarativa, sem o incômodo de obter o verificar a ativação ou pensar sobre a API de comunicação subjacente:
wb.messageSW({"type": "PREFETCH", "payload": {"urls": ["/data1.json", "data2.json"]}}); });
O service worker implementa um gerenciador message
para
ouvir essas mensagens. Ele pode, opcionalmente, retornar uma resposta, mas, em casos como esses, é
não é necessário:
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PREFETCH') {
// do something
}
});
Como usar as APIs do navegador
Se a biblioteca Workbox não for suficiente para suas necessidades, confira como implementar a janela ao serviço comunicação do worker usando APIs de navegador.
A API postMessage pode ser usado para estabelecer um mecanismo de comunicação unidirecional da página com o service worker.
A página chama
postMessage()
no
interface do service worker:
navigator.serviceWorker.controller.postMessage({
type: 'MSG_ID',
payload: 'some data to perform the task',
});
O service worker implementa um gerenciador message
para
ouvir essas mensagens.
self.addEventListener('message', (event) => {
if (event.data && event.data.type === MSG_ID) {
// do something
}
});
O atributo {type : 'MSG_ID'}
não é absolutamente obrigatório, mas é uma maneira de permitir que a página
enviar diferentes tipos de instruções para o service worker (ou seja, "para pré-busca" vs. "para limpar
armazenamento"). O service worker pode se ramificar em diferentes caminhos de execução com base nessa flag.
Se a operação tiver sido bem-sucedida, o usuário poderá aproveitar os benefícios, mas, se não tiver, o fluxo principal do usuário não será alterado. Por exemplo, quando 1-800-Flowers.com tenta pré-armazenar em cache, a página não precisa saber se o service worker foi bem-sucedido. Se tiver, o usuário desfrutará de uma navegação mais rápida. Caso contrário, ainda será necessário acessar a nova página. Só vai demorar um pouco mais.
Um exemplo simples de pré-busca
Uma das aplicações mais comuns do armazenamento em cache imperativo é a pré-busca, que significa buscar recursos de um determinado URL, antes que o usuário vá até ele, a fim de acelerar a navegação.
Há maneiras diferentes de implementar a pré-busca em sites:
- Usar tags de pré-busca de links em páginas: os recursos são mantidos no
cache do navegador por cinco minutos. Depois disso, as regras
Cache-Control
normais para o recurso se aplicam. - Complementando a técnica anterior com uma estratégia de armazenamento em cache no ambiente de execução no serviço worker para estender o ciclo de vida da pré-busca recurso além desse limite.
Para cenários de pré-busca relativamente simples, como pré-busca de documentos ou recursos específicos (JS, CSS etc.), essas técnicas são a melhor abordagem.
Se uma lógica adicional for necessária, por exemplo, analisar o recurso de pré-busca (um arquivo ou página JSON) em para buscar seus URLs internos, é mais apropriado delegar essa tarefa inteiramente ao service worker.
Delegar esses tipos de operações ao service worker tem as seguintes vantagens:
- Descarregando o trabalho pesado de busca e processamento pós-busca (que serão apresentados mais tarde) para um thread secundário. Ao fazer isso, ela libera a linha de execução principal para lidar com tarefas tarefas como responder às interações do usuário.
- Permitir que vários clientes (por exemplo, guias) reutilizem uma funcionalidade comum e até mesmo chamarem o sem bloquear a linha de execução principal.
Fazer uma pré-busca das páginas de detalhes do produto
Primeiro, use postMessage()
em
interface do service worker e passe uma matriz de URLs para armazenar em cache:
navigator.serviceWorker.controller.postMessage({
type: 'PREFETCH',
payload: {
urls: [
'www.exmaple.com/apis/data_1.json',
'www.exmaple.com/apis/data_2.json',
],
},
});
No service worker, implemente um gerenciador message
para
interceptar e processar mensagens enviadas por qualquer guia ativa:
addEventListener('message', (event) => {
let data = event.data;
if (data && data.type === 'PREFETCH') {
let urls = data.payload.urls;
for (let i in urls) {
fetchAsync(urls[i]);
}
}
});
No código anterior, introduzimos uma pequena função auxiliar chamada fetchAsync()
para iterar na
matriz de URLs e emitirá uma solicitação de busca para cada um deles:
async function fetchAsync(url) {
// await response of fetch call
let prefetched = await fetch(url);
// (optionally) cache resources in the service worker storage
}
Quando a resposta é recebida, você pode confiar nos cabeçalhos de armazenamento em cache do recurso. Em muitos casos,
no entanto, como nas páginas de detalhes do produto, os recursos não são armazenados em cache, ou seja,
cabeçalho Cache-control
de no-cache
). Em casos como esses, você pode substituir esse comportamento,
armazenar o recurso buscado no cache do service worker. Isso tem a vantagem adicional de permitir que
seja exibido em cenários off-line.
Além dos dados JSON
Quando os dados JSON são buscados em um endpoint do servidor, eles geralmente contêm outros URLs que também são que vale a pré-busca, como uma imagem ou outros dados de endpoint associados a esse dados.
Digamos que, em nosso exemplo, os dados JSON retornados sejam as informações de um site de compras de supermercado:
{
"productName": "banana",
"productPic": "https://cdn.example.com/product_images/banana.jpeg",
"unitPrice": "1.99"
}
Modifique o código fetchAsync()
para iterar a lista de produtos e armazenar em cache a imagem principal para
para cada um deles:
async function fetchAsync(url, postProcess) {
// await response of fetch call
let prefetched = await fetch(url);
//(optionally) cache resource in the service worker cache
// carry out the post fetch process if supplied
if (postProcess) {
await postProcess(prefetched);
}
}
async function postProcess(prefetched) {
let productJson = await prefetched.json();
if (productJson && productJson.product_pic) {
fetchAsync(productJson.product_pic);
}
}
Você pode adicionar alguma manipulação de exceção nesse código para situações como 404s. Mas a beleza de usar um service worker para fazer a pré-busca é que ele pode falhar sem muito consequência para a página e a linha de execução principal. Você também pode ter uma lógica mais elaborada no pós-processamento do conteúdo pré-buscado, tornando-o mais flexível e desacoplado dos dados processamento. O céu é o limite.
Conclusão
Neste artigo, abordamos um caso de uso comum de comunicação unidirecional entre página e serviço. worker: armazenamento em cache imperativo. Os exemplos discutidos servem apenas para demonstrar uma forma de usando esse padrão e a mesma abordagem também pode ser aplicada a outros casos de uso, por exemplo, armazenamento em cache dos principais artigos sob demanda para consumo off-line, adição aos favoritos, entre outros.
Para mais padrões de comunicação de service worker e páginas, confira:
- 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).
- Comunicação bidirecional: delegar uma tarefa a um service worker (por exemplo, um download pesado) e manter a página informada sobre o progresso.