Armazenamento em cache

O armazenamento em cache é uma ferramenta poderosa. Isso torna seus apps menos dependentes das condições de rede. Com um bom uso dos caches, é possível disponibilizar o app da Web off-line e veicular os 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.
  • Qualquer outro worker que você usa.

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 capacidade de funcionar off-line seja um dos requisitos para um app da Web progressivo, é essencial entender que nem todo PWA precisa 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.

Sua 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 todos os recursos do app forem transferidos rapidamente, não consumirem muito espaço e não precisarem ser atualizados em cada solicitação, o armazenamento em cache de todos os recursos seria uma estratégia válida. 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 baseada 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. Você vai aprender a 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 maior número possível de recursos quando o service worker for instalado, geralmente 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á inativa.

É 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 é 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 a 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 app entre domínios, a interação do 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, o que significa que seu código não poderá acessar 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).

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

Depurar o armazenamento em 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 abordar mais sobre essas ferramentas no capítulo "Ferramentas e depuração".

Depuração do Chrome DevTools no conteúdo do armazenamento em cache.

Recursos