Armazenamento em cache

O armazenamento em cache é uma ferramenta poderosa. Ela torna seus apps menos dependentes das condições de rede. Com um bom uso dos caches, você pode disponibilizar seu aplicativo da Web off-line e exibir seus recursos o mais rápido possível em qualquer condição de rede. Como 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.

Compatibilidade com navegadores

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

Origem

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.
  • Quaisquer outros workers que você usar.

Uma das vantagens 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 pergunta que você pode ter sobre o armazenamento em cache é o que armazenar em cache. 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 com várias páginas, outros documentos HTML que você quer exibir rapidamente ou enquanto estiver off-line.

Pronto para off-line

Embora a funcionalidade off-line seja um dos requisitos de um Progressive Web App, é essencial entender que nem todos os PWAs precisam de uma experiência off-line completa, por exemplo, soluções de jogos em nuvem ou apps de criptoativos. Portanto, é aceitável oferecer uma interface básica para orientar os usuários nessas situações.

A PWA não deve 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 worker de serviço para mostrar suas próprias mensagens, evitando um erro genérico e confuso do navegador.

Há muitas estratégias de armazenamento em cache diferentes que podem ser usadas, dependendo das necessidades da sua PWA. Por isso, é importante projetar o uso do cache para oferecer uma experiência rápida e confiável. Por exemplo, se o download de todos os recursos de link para app for rápido, não consumir muito espaço e não precisarem ser atualizados em todas as solicitações, armazenar todos os recursos em cache é uma boa estratégia. Por outro lado, se você tiver recursos que precisam ser a versão mais recente, talvez seja melhor não armazenar esses recursos em cache.

Como usar a API

Use a API Cache Storage para definir um conjunto de caches na 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 solicitar que o navegador 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 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 de cache armazena a totalidade de uma resposta, incluindo todos os cabeçalhos e o corpo. Por isso, é possível recuperá-lo mais tarde usando uma solicitação HTTP ou um URL como chave. Confira como fazer isso no capítulo "Servir".

Quando armazenar em cache

No PWA, você é responsável por decidir quando armazenar arquivos em cache. Embora uma abordagem seja armazenar o máximo de recursos possível quando o service worker está instalado, essa normalmente não é a melhor ideia. Armazenar em cache recursos desnecessários desperdiça largura de banda e espaço de armazenamento e pode fazer com que o app forneça recursos desatualizados indesejados.

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

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

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

Armazenar recursos em cache em um service worker

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

Como a linha de execução do service worker pode ser interrompida a qualquer momento, é possível solicitar que o navegador aguarde a conclusão da promessa addAll para aumentar a oportunidade de armazenar todos os recursos e manter a consistência do app. O exemplo a seguir demonstra 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 que aguarde a resolução da tarefa na promessa (cumprida 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 assíncrona/de espera. Nesse caso, você precisa criar uma função assíncrona que possa chamar await e retornar uma promessa para waitUntil() após ser 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 fazer o download e armazenar em cache recursos da origem e de domínios diferentes, como conteúdo de CDNs de terceiros. Com um aplicativo de vários domínios, a interação com o cache é muito semelhante às solicitações de mesma origem. A solicitação é executada e uma cópia da resposta é armazenada no cache. Assim como outros recursos em cache, ele só está disponível para uso na origem do app.

O recurso será armazenado como uma resposta opaca, ou seja, 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 de armazenamento, afetando as cotas. Alguns navegadores expõem 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 Cache para mais detalhes.

Depurar o armazenamento de cache

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

Chrome DevTools depurando conteúdo do armazenamento em cache.

Recursos