Disponibilizando

Um aspecto importante dos Progressive Web Apps é que eles são confiáveis. Eles podem carregar ativos rapidamente, mantendo os usuários engajados e oferecendo feedback imediatamente, mesmo em condições ruins de rede. Como isso é possível? Graças ao evento fetch do service worker.

O evento de busca

Compatibilidade com navegadores

  • 40
  • 17
  • 44
  • 11.1

Origem

O evento fetch permite interceptar todas as solicitações de rede feitas pelo PWA no escopo do service worker para solicitações da mesma origem e entre origens. Além de solicitações de navegação e recursos, a busca de um service worker instalado permite que 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.

O service worker fica entre o cliente e a rede.

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 seu service worker, há duas coisas que você pode fazer: você pode ignorar, o que permite que ela vá para a rede, ou você pode responder a ela. Responder a solicitações no service worker é uma maneira 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() em um manipulador de eventos fetch da seguinte 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. Mas não é possível chamar respondWith() depois que o manipulador de eventos de busca é concluído, como em uma chamada assíncrona. Se for necessário aguardar a resposta completa, é possível transmitir uma promessa para respondWith() que seja resolvida com uma resposta.

Criar respostas

Graças à API Fetch, é possível criar respostas HTTP no código JavaScript, e essas respostas podem ser armazenadas em cache usando a API Cache Storage e retornadas como se fossem de um servidor da Web.

Para gerar uma resposta, crie um novo objeto Response, definindo o corpo e opções 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 a partir do cache

Agora que você sabe como exibir 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, responder a respondWith() com ela. Para fazer isso, você precisa primeiro pesquisar no cache. A função match(), disponível na interface de nível superior caches, pesquisa todas as lojas 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

A disponibilização de arquivos apenas do cache do navegador não é adequada para 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 intervalos diferentes para padrões de URL distintos. Por exemplo, você pode ter uma estratégia para os recursos mínimos de interface, outra para chamadas de API e uma terceira para URLs de imagem e dados. Para fazer isso, leia event.request.url em ServiceWorkerGlobalScope.onfetch e analise usando expressões regulares ou um padrão de URL. Até o momento, o padrão de URL não é compatível com todas as plataformas.

As estratégias mais comuns são:

Armazenar em cache primeiro
Procura primeiro uma resposta em cache e volta para a rede se não for encontrada.
Priorização da rede
Solicita uma resposta da rede primeiro e, se nenhuma for retornada, verifica a resposta no cache.
Desatualizado ao revalidar
Fornece uma resposta do cache, solicita a versão mais recente em segundo plano 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.

Cache primeiro

Com 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 nem de rede, a solicitação apresentará um erro. Como a veiculação de recursos sem acessar a rede tende a ser mais rápida, essa estratégia prioriza a performance em vez da atualização.

A estratégia que prioriza o cache

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);
     }
   )
  )
});

Prioridade da rede

Essa estratégia é o espelho da estratégia de cache priorizando. Ela verifica se a solicitação pode ser atendida pela rede e, se não pode, tenta recuperá-la do cache. Marque 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 a do cache. Essa estratégia prioriza o conteúdo atualizado em vez do desempenho.

A estratégia que prioriza a rede

self.addEventListener("fetch", event => {
   event.respondWith(
     fetch(event.request)
     .catch(error => {
       return caches.match(event.request) ;
     })
   );
});

Desatualizado ao revalidar

A estratégia descontinuada ao revalidar retorna imediatamente uma resposta armazenada em cache e, em seguida, verifica se há uma atualização na rede, substituindo a resposta armazenada em cache, se ela for encontrada. Essa estratégia sempre faz uma solicitação de rede, porque mesmo se um recurso armazenado em cache for encontrado, ele vai 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 se beneficiar da veiculação rápida da estratégia que prioriza o cache e atualizar o cache em segundo plano.

Estratégia desatualizada ao revalidar

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ó vão retornar um recurso se ele puder ser buscado na rede. Isso geralmente é útil para recursos como solicitações de API somente on-line.

A estratégia &quot;Somente rede&quot;

Somente cache

A estratégia somente em 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égia somente de cache.

Estratégias personalizadas

Embora as estratégias acima sejam comuns, você é responsável pelo service worker e como as solicitações são processadas. Se nenhuma dessas opções funcionar para suas necessidades, crie a sua.

É possível, por exemplo, usar uma estratégia de priorização da rede com tempo limite para priorizar o conteúdo atualizado, mas somente se a resposta aparecer dentro de um limite definido por você. Também é possível mesclar uma resposta armazenada em cache com uma resposta de rede e criar uma resposta complexa a partir do service worker.

Atualizando recursos

Manter os recursos em cache do PWA atualizados pode ser um desafio. Embora a estratégia desatualizada enquanto revalide seja uma maneira de fazer isso, não é a única. No capítulo "Atualizar", você vai aprender diferentes técnicas para manter o conteúdo e os recursos do app atualizados.

Recursos