Disponibilização

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.

Compatibilidade com navegadores

  • Chrome: 40.
  • Borda: 17.
  • Firefox: 44.
  • Safari: 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 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.

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 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.

A estratégia de priorização de 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);
     }
   )
  )
});

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.

A estratégia que prioriza a rede

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.

A 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ó retornarão 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;Apenas rede&quot;

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

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.

Recursos