Armazenamento em cache

O armazenamento em cache é uma ferramenta poderosa. Isso torna seus apps menos dependentes das condições da rede. Com o uso adequado de caches, é possível disponibilizar seu app da Web off-line e veicular seus recursos o mais rápido possível em qualquer condição de rede. Conforme mencionado em Recursos e dados, você pode decidir a melhor estratégia para armazenar em cache os recursos necessários. Para gerenciar o cache, o service worker interage com a API Cache Storage.

Browser Support

  • Chrome: 43.
  • Edge: 16.
  • Firefox: 41.
  • Safari: 11.1.

Source

A API Cache Storage está disponível em diferentes contextos:

  • O contexto da janela (a linha de execução principal do PWA).
  • O service worker.
  • Outros trabalhadores que você usa.

Uma vantagem de gerenciar o cache usando service workers é que o ciclo de vida dele não está vinculado à janela, o que significa que você não está bloqueando a linha de execução principal. Para usar a API Cache Storage, a maioria desses contextos precisa estar em uma conexão TLS.

O que armazenar em cache

A primeira dúvida que você pode ter sobre o cache é o que armazenar. Embora não haja uma única resposta para essa pergunta, você pode começar com todos os recursos mínimos necessários para renderizar a interface do usuário.

Esses recursos precisam incluir:

  • O HTML da página principal (start_url do seu app).
  • Folhas de estilo CSS necessárias para a interface principal do usuário.
  • Imagens usadas na interface do usuário.
  • Arquivos JavaScript necessários para renderizar a interface do usuário.
  • Dados, como um arquivo JSON, necessários para renderizar uma experiência básica.
  • Fontes da Web.
  • Em um aplicativo de várias páginas, outros documentos HTML que você quer veicular rapidamente ou off-line.

Pronto para o modo off-line

Embora a capacidade de funcionar off-line seja um dos requisitos de um Progressive Web App, é essencial entender que nem todo PWA precisa de uma experiência off-line completa, como soluções de jogos na nuvem ou apps de criptoativos. Portanto, não há problema em oferecer uma interface básica que oriente os usuários nessas situações.

O PWA não pode renderizar uma mensagem de erro do navegador informando que o mecanismo de renderização da Web não conseguiu carregar a página. Em vez disso, use o service worker para mostrar suas próprias mensagens, evitando um erro genérico e confuso do navegador.

Há muitas estratégias de cache diferentes que você pode usar, dependendo das necessidades do seu PWA. Por isso, é importante projetar o uso do cache para oferecer uma experiência rápida e confiável. Por exemplo, se todos os recursos do app forem baixados rapidamente, não consumirem muito espaço e não precisarem ser atualizados em todas as solicitações, o armazenamento em cache de todos os recursos será uma estratégia válida. Por outro lado, se você tiver recursos que precisam ser da versão mais recente, considere não armazenar esses recursos em cache.

Como usar a API

Use a API Cache Storage para definir um conjunto de caches na sua origem, cada um identificado com um nome de string que você pode definir. Acesse a API pelo objeto caches, e o método open permite a criação ou abertura de um cache já criado. O método "open" retorna uma promessa para o objeto de cache.

caches.open("pwa-assets")
.then(cache => {
  // you can download and store, delete or update resources with cache arguments
});

Como fazer o download e armazenar recursos

Para pedir ao navegador que faça o download e armazene os recursos, use os métodos add ou addAll. O método add faz uma solicitação e armazena uma resposta HTTP, e o addAll, um grupo de respostas HTTP como uma transação com base em uma matriz de solicitações ou URLs.

caches.open("pwa-assets")
.then(cache => {
  cache.add("styles.css"); // it stores only one resource
  cache.addAll(["styles.css", "app.js"]); // it stores two resources
});

A interface de armazenamento em cache armazena toda a resposta, incluindo todos os cabeçalhos e o corpo. Consequentemente, é possível recuperá-lo mais tarde usando uma solicitação HTTP ou um URL como chave. Você vai aprender a fazer isso no capítulo "Serviço".

Quando armazenar em cache

No seu PWA, você decide quando armazenar arquivos em cache. Embora uma abordagem seja armazenar o máximo de recursos possível quando o service worker é instalado, geralmente não é a melhor ideia. O armazenamento em cache de recursos desnecessários desperdiça largura de banda e espaço de armazenamento e pode fazer com que o app disponibilize recursos desatualizados não intencionais.

Não é necessário armazenar em cache todos os recursos de uma vez. É possível fazer isso várias vezes durante o ciclo de vida do PWA, como:

  • Na instalação do service worker.
  • Após o primeiro carregamento da página.
  • Quando o usuário navega até uma seção ou rota.
  • Quando a rede está inativa.

É possível solicitar o armazenamento em cache de novos arquivos na linha de execução principal ou no contexto do service worker.

Armazenamento em cache de recursos em um service worker

Um dos cenários mais comuns é armazenar em cache um conjunto mínimo de recursos quando o service worker é instalado. Para isso, use a interface de armazenamento em cache no evento install no service worker.

Como a linha de execução do service worker pode ser interrompida a qualquer momento, você pode pedir ao navegador para aguardar a conclusão da promessa addAll e aumentar a oportunidade de armazenar todos os recursos e manter a consistência do app. O exemplo a seguir mostra como fazer isso usando o método waitUntil do argumento de evento recebido no listener de eventos do service worker.

const urlsToCache = ["/", "app.js", "styles.css", "logo.svg"];
self.addEventListener("install", event => {
   event.waitUntil(
      caches.open("pwa-assets")
      .then(cache => {
         return cache.addAll(urlsToCache);
      });
   );
});

O método waitUntil() recebe uma promessa e pede ao navegador para aguardar a resolução da tarefa na promessa (concluída ou com falha) antes de encerrar o processo do service worker. Talvez seja necessário encadear promessas e retornar as chamadas add() ou addAll() para que um único resultado chegue ao método waitUntil().

Também é possível processar promessas usando a sintaxe async/await. Nesse caso, você precisa criar uma função assíncrona que possa chamar await e que retorne uma promessa para waitUntil() depois que ela for chamada, como no exemplo a seguir:

const urlsToCache = ["/", "app.js", "styles.css", "logo.svg"];
self.addEventListener("install", (event) => {
   let cacheUrls = async () => {
      const cache = await caches.open("pwa-assets");
      return cache.addAll(urlsToCache);
   };
   event.waitUntil(cacheUrls());
});

Solicitações entre domínios e respostas opacas

O PWA pode baixar e armazenar em cache recursos da sua origem e de domínios cruzados, como conteúdo de CDNs de terceiros. Com um app entre domínios, a interação do cache é muito semelhante às solicitações da mesma origem. A solicitação é executada, e uma cópia da resposta é armazenada no cache. Assim como outros recursos armazenados em cache, ele só pode ser usado na origem do seu app.

O recurso será armazenado como uma resposta opaca, o que significa que seu código não poderá ver nem modificar o conteúdo ou os cabeçalhos dessa resposta. Além disso, as respostas opacas não expõem o tamanho real na API Storage, afetando as cotas. Alguns navegadores mostram tamanhos grandes, como 7 MB, mesmo que o arquivo tenha apenas 1 KB.

Atualizar e excluir recursos

É possível atualizar recursos usando cache.put(request, response) e excluir recursos com delete(request).

Confira a documentação do objeto de cache para mais detalhes.

Depuração do armazenamento em cache

Muitos navegadores oferecem uma maneira de depurar o conteúdo do armazenamento em cache na guia "Aplicativo" das DevTools. Lá, você pode conferir o conteúdo de cada cache na origem atual. Vamos falar mais sobre essas ferramentas no capítulo Ferramentas e depuração.

O Chrome DevTools depura o conteúdo do Cache Storage.

Recursos