Um aspecto importante dos Progressive Web Apps é que eles são confiáveis. eles podem carregar recursos rapidamente, mantendo os usuários engajados e fornecendo feedback imediatamente, mesmo em más condições de rede. Como isso é possível? Graças ao evento fetch
do service worker.
O evento de busca
O evento fetch
permite interceptar todas as solicitações de rede feitas pelo PWA no escopo do service worker para solicitações de mesma origem e entre origens. Além da navegação e das solicitações de recursos, a busca em um service worker instalado permite que as visitas à página após o primeiro carregamento de um site sejam renderizadas sem chamadas de rede.
O gerenciador fetch
recebe todas as solicitações de um app, incluindo URLs e cabeçalhos HTTP, e permite que o desenvolvedor do app decida como processá-las.
Seu service worker pode encaminhar uma solicitação para a rede, responder com uma resposta previamente armazenada em cache ou criar uma nova resposta. A escolha é sua. Veja um exemplo simples:
self.addEventListener("fetch", event => {
console.log(`URL requested: ${event.request.url}`);
});
Como responder a uma solicitação
Quando uma solicitação chega ao service worker, é possível fazer duas coisas: Você pode ignorá-lo, o que permite que ele vá para a rede, ou você pode responder a ele. Responder a solicitações de dentro do service worker é uma forma de escolher o que e como ele é retornado ao PWA, mesmo quando o usuário está off-line.
Para responder a uma solicitação recebida, chame event.respondWith()
de dentro de um manipulador de eventos fetch
, desta forma:
// fetch event handler in your service worker file
self.addEventListener("fetch", event => {
const response = .... // a response or a Promise of response
event.respondWith(response);
});
Chame respondWith()
de forma síncrona e retorne um objeto Response. No entanto, não será possível chamar respondWith()
depois que o manipulador de eventos de busca for concluído, como em uma chamada assíncrona. Se você precisar aguardar a resposta completa, transmita uma promessa ao respondWith()
que será resolvida com uma resposta.
Como criar respostas
Graças à API Fetch, é possível criar respostas HTTP no código JavaScript. Essas respostas podem ser armazenadas em cache usando a API Cache Storage e retornadas como se vierem de um servidor da Web.
Para gerar uma resposta, crie um objeto Response
, definindo o corpo e as opções dele, como status e cabeçalhos:
const simpleResponse = new Response("Body of the HTTP response");
const options = {
status: 200,
headers: {
'Content-type': 'text/html'
}
};
const htmlResponse = new Response("<b>HTML</b> content", options)
Como responder usando o cache
Agora que você sabe como disponibilizar respostas HTTP de um service worker, é hora de usar a interface de armazenamento em cache para armazenar recursos no dispositivo.
Você pode usar a API de armazenamento em cache para verificar se a solicitação recebida do PWA está disponível no cache e, se estiver, responda a respondWith()
com ela.
Para fazer isso, primeiro você precisa pesquisar no cache. A função match()
, disponível na interface caches
de nível superior, pesquisa todos os armazenamentos na sua origem ou em um único objeto de cache aberto.
A função match()
recebe uma solicitação HTTP ou um URL como argumento e retorna uma promessa que é resolvida com a resposta associada à chave correspondente.
// Global search on all caches in the current origin
caches.match(urlOrRequest).then(response => {
console.log(response ? response : "It's not in the cache");
});
// Cache-specific search
caches.open("pwa-assets").then(cache => {
cache.match(urlOrRequest).then(response => {
console.log(response ? response : "It's not in the cache");
});
});
Estratégias de armazenamento em cache
Veicular arquivos somente a partir do cache do navegador não se enquadra em todos os casos de uso. Por exemplo, o usuário ou o navegador pode remover o cache. É por isso que você deve definir suas próprias estratégias de envio de recursos para seu PWA.
Você não está restrito a uma estratégia de armazenamento em cache. É possível definir diferentes para padrões de URL diferentes. Por exemplo, é possível ter uma estratégia para os recursos mínimos de interface, outra para chamadas de API e uma terceira para URLs de dados e imagens.
Para fazer isso, leia event.request.url
no ServiceWorkerGlobalScope.onfetch
e analise-o usando expressões regulares ou um padrão de URL. Atualmente, não havia suporte para o padrão de URL em todas as plataformas.
As estratégias mais comuns são:
- Priorizar cache
- Procura uma resposta em cache primeiro e volta à rede se não for encontrada.
- Priorizar a rede
- Solicita primeiro uma resposta da rede e, se nenhuma for retornada, verifica a resposta no cache.
- Desatualizado ao revalidar
- Disponibiliza uma resposta do cache, enquanto que em segundo plano solicita a versão mais recente e a salva no cache para a próxima vez que o recurso for solicitado.
- Somente rede
- Sempre responde com uma resposta da rede ou erros. O cache nunca é consultado.
- Somente cache
- Sempre responde com uma resposta do cache ou erros. A rede nunca será consultada. Os recursos que serão veiculados com essa estratégia precisam ser adicionados ao cache antes de serem solicitados.
Armazenar em cache primeiro
Usando essa estratégia, o service worker procura a solicitação correspondente no cache e retorna a Resposta correspondente se ela estiver armazenada. Caso contrário, ele recupera a resposta da rede (opcionalmente, atualizando o cache para chamadas futuras). Se não houver uma resposta de cache ou de rede, a solicitação apresentará um erro. Como costuma ser mais rápido veicular recursos sem acessar a rede, essa estratégia prioriza a performance em vez da atualização.
self.addEventListener("fetch", event => {
event.respondWith(
caches.match(event.request)
.then(cachedResponse => {
// It can update the cache to serve updated content on the next request
return cachedResponse || fetch(event.request);
}
)
)
});
Rede em primeiro lugar
Essa estratégia é o espelho da estratégia Cache First. ele verifica se a solicitação pode ser atendida pela rede e, se não puder, tenta recuperá-la do cache. Como o cache primeiro. Se não houver uma resposta de rede nem de cache, a solicitação apresentará um erro. Receber a resposta da rede geralmente é mais lenta do que recebê-la do cache. Essa estratégia prioriza o conteúdo atualizado em vez do desempenho.
self.addEventListener("fetch", event => {
event.respondWith(
fetch(event.request)
.catch(error => {
return caches.match(event.request) ;
})
);
});
Desatualizado durante a revalidação
A estratégia desatualizada durante a revalidação retorna imediatamente uma resposta armazenada em cache e, em seguida, verifica se há uma atualização na rede, substituindo a resposta armazenada em cache, caso ela seja encontrada. Essa estratégia sempre faz uma solicitação de rede, porque mesmo que um recurso armazenado em cache seja encontrado, ela tentará atualizar o que estava no cache com o que foi recebido da rede para usar a versão atualizada na próxima solicitação. Portanto, essa estratégia oferece uma maneira de você se beneficiar da exibição rápida da estratégia de cache primeiro e atualizar o cache em segundo plano.
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cachedResponse => {
const networkFetch = fetch(event.request).then(response => {
// update the cache with a clone of the network response
const responseClone = response.clone()
caches.open(url.searchParams.get('name')).then(cache => {
cache.put(event.request, responseClone)
})
return response
}).catch(function (reason) {
console.error('ServiceWorker fetch failed: ', reason)
})
// prioritize cached response over network
return cachedResponse || networkFetch
}
)
)
})
Somente rede
A estratégia "Somente rede" é semelhante a como os navegadores se comportam sem um service worker ou a API Cache Storage. As solicitações só retornarão um recurso se ele puder ser buscado na rede. Isso geralmente é útil para recursos como solicitações de API somente on-line.
Somente cache
A estratégia de somente cache garante que as solicitações nunca cheguem à rede. todas as solicitações recebidas serão respondidas com um item de cache pré-preenchido. O código a seguir usa o manipulador de eventos fetch
com o método match
do armazenamento em cache para responder apenas ao cache:
self.addEventListener("fetch", event => {
event.respondWith(caches.match(event.request));
});
Estratégias personalizadas
As estratégias de armazenamento em cache acima são comuns, mas você é responsável pelo service worker e como as solicitações são tratadas. Se nenhum desses recursos funcionar para suas necessidades, crie suas próprias opções.
Por exemplo, é possível usar uma estratégia que prioriza a rede com um tempo limite para priorizar o conteúdo atualizado, mas somente se a resposta aparecer dentro de um limite definido por você. Você também pode mesclar uma resposta em cache com uma resposta de rede e criar uma resposta complexa do service worker.
Atualizando recursos
Manter os recursos em cache do seu PWA atualizados pode ser um desafio. Embora a estratégia desatualizada durante a revalidação seja uma maneira de fazer isso, ela não é a única. No capítulo Atualização, você vai aprender diferentes técnicas para manter o conteúdo e os recursos do seu app atualizados.