Disponibilização

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

Browser Support

  • Chrome: 40.
  • Edge: 17.
  • Firefox: 44.
  • Safari: 11.1.

Source

O evento fetch permite interceptar todas as solicitações de rede feitas pelo PWA no escopo do worker de serviço, para solicitações de mesma origem e de origem cruzada. Além das solicitações de navegação e 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 worker de serviço fica entre o cliente e a rede.

O service worker pode encaminhar uma solicitação para a rede, responder com uma resposta armazenada em cache anteriormente 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 worker de serviço, você pode fazer duas coisas: ignorá-la, o que permite que ela vá para a rede, ou responder a ela. A resposta às solicitações do service worker é como você pode 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, como este:

// fetch event handler in your service worker file
self.addEventListener("fetch", event => {
    const response = .... // a response or a Promise of response
    event.respondWith(response);
});

É necessário chamar respondWith() de forma síncrona e retornar um objeto Response. Mas não é possível chamar respondWith() depois que o manipulador de eventos de busca for concluído, como em uma chamada assíncrona. Se você precisar esperar pela resposta completa, transmita uma promessa para respondWith() que seja 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 viessem de um servidor da Web.

Para criar uma resposta, crie um novo objeto Response e defina o corpo e as 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)

Respostas do cache

Agora que você sabe como veicular respostas HTTP de um service worker, é hora de usar a interface de armazenamento de cache para armazenar recursos no dispositivo.

É possível usar a API de armazenamento em cache para verificar se a solicitação recebida do PWA está disponível no cache. Se estiver, responda a respondWith() com ela. Para isso, primeiro você precisa pesquisar no cache. A função match(), disponível na interface caches de nível superior, 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

O envio de arquivos apenas do cache do navegador não é adequado para todos os casos de uso. Por exemplo, o usuário ou o navegador pode remover o cache. Por isso, você precisa definir suas próprias estratégias para fornecer recursos para a PWA. Você não fica restrito a uma estratégia de armazenamento em cache. Você pode definir padrões diferentes para padrões de URL diferentes. Por exemplo, você pode ter uma estratégia para os recursos mínimos da interface, outra para as chamadas de API e uma terceira para URLs de imagens e dados. Para fazer isso, leia event.request.url em ServiceWorkerGlobalScope.onfetch e analise-o usando expressões regulares ou um padrão de URL. No momento, o Padrão de URL não é compatível com todas as plataformas.

As estratégias mais comuns são:

Cache First
Procura primeiro uma resposta armazenada em cache e volta para a rede se nenhuma for encontrada.
Rede em primeiro lugar
Solicita uma resposta da rede primeiro e, se nenhuma for retornada, verifica a resposta no cache.
Desatualizado durante a revalidação
Exibe uma resposta do cache, enquanto 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 gera um erro. O cache nunca é consultado.
Somente cache
Sempre responde com uma resposta do cache ou gera erros. A rede nunca será consultada. Os recursos que serão veiculados usando essa estratégia precisam ser adicionados ao cache antes de serem solicitados.

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 em cache. Caso contrário, ele recupera a resposta da rede (se preferir, atualizando o cache para chamadas futuras). Se não houver uma resposta de cache nem de rede, a solicitação vai gerar 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 atualidade.

Estratégia 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". Ela verifica se a solicitação pode ser atendida pela rede e, se não for possível, tenta recuperá-la do cache. Como o cache primeiro. Se não houver uma resposta de rede nem de cache, a solicitação vai gerar um erro. A resposta da rede geralmente é mais lenta do que a do cache. Essa estratégia prioriza o conteúdo atualizado em vez da performance.

Estratégia com foco em 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 de revalidação de dados desaturos retorna uma resposta em cache imediatamente e, em seguida, verifica a rede em busca de uma atualização, substituindo a resposta em cache, se encontrada. Essa estratégia sempre faz uma solicitação de rede, porque, mesmo que um recurso em cache seja 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. Essa estratégia oferece uma maneira de você aproveitar a exibição rápida da estratégia de cache primeiro e atualizar o cache em segundo plano.

A estratégia de revalidação de conteúdo desatualizado

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 de rede exclusiva é semelhante ao comportamento dos navegadores sem um service worker ou a API Cache Storage. As solicitações só vão retornar um recurso se ele puder ser buscado da rede. Isso geralmente é útil para recursos como solicitações de API somente on-line.

Estratégia somente de rede

Somente cache

A estratégia de uso exclusivo do cache garante que as solicitações nunca vão para a rede. Todas as solicitações recebidas são respondidas com um item de cache preenchido. O código abaixo usa o manipulador de eventos fetch com o método match do armazenamento de 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 de armazenamento em cache acima sejam comuns, você é responsável pelo service worker e pela forma como as solicitações são tratadas. Se nenhuma delas atender às suas necessidades, crie a sua.

Por exemplo, é possível usar uma estratégia de rede em primeiro lugar com um tempo limite para priorizar o conteúdo atualizado, mas somente se a resposta aparecer dentro de um limite definido. Também é possível mesclar uma resposta em cache com uma resposta de rede e criar uma resposta complexa do service worker.

Como atualizar recursos

Manter os recursos em cache da sua PWA atualizados pode ser um desafio. Embora a estratégia de revalidação de dados desaturados seja uma maneira de fazer isso, não é a única. No capítulo de atualização, você vai aprender diferentes técnicas para manter o conteúdo e os recursos do app atualizados.

Recursos