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, é possível escolher a melhor estratégia para armazenar os recursos necessários em cache. Para gerenciar o cache, o service worker interage com a API Cache Storage.

Compatibilidade com navegadores

  • Chrome: 43.
  • Borda: 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 vantagem de gerenciar seu 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 sob uma conexão TLS.

O que armazenar em cache

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

Esses recursos devem incluir:

  • O HTML da página principal (o start_url do seu app).
  • Folhas de estilo CSS necessárias para a interface de usuário principal.
  • 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ê deseja exibir de forma rápida ou 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, não há problema em oferecer uma interface básica para guiar os usuários nessas situações.

Seu PWA não deve renderizar a mensagem de erro de um 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 diferentes de armazenamento em cache que podem ser usadas de acordo com as necessidades do 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 estar na 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 definido por você. Acesse a API com o objeto caches. O método open permite criar ou abrir um cache já criado. O método open retorna uma promessa para o objeto do 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 em cache armazena toda a resposta, incluindo todos os cabeçalhos e o corpo. Consequentemente, você poderá recuperá-lo posteriormente usando uma solicitação HTTP ou um URL como chave. Saiba como fazer isso no capítulo "Exibição".

Quando armazenar em cache

No seu PWA, você é responsável por decidir quando armazenar os 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. O armazenamento em cache de recursos desnecessários desperdiça largura de banda e espaço de armazenamento e pode fazer com que o aplicativo exiba recursos desatualizados não intencionais.

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

  • Na instalação do service worker.
  • Após o primeiro carregamento de 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.

Como 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 em cache no evento install do service worker.

Como a linha de execução do service worker pode ser interrompida a qualquer momento, você pode solicitar que o navegador aguarde a conclusão da promessa addAll para aumentar a oportunidade de armazenar todos os recursos e manter o app consistente. 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 tarefa na promessa ser resolvida (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().

Você também pode processar promessas usando a sintaxe async/await. Nesse caso, é necessário criar uma função assíncrona que possa chamar await e que retorne uma promessa para waitUntil() depois de 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 os recursos da sua origem e de vários domínios, 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 em seu cache. Assim como acontece com outros recursos em cache, ele só está disponível para uso na origem do seu app.

O recurso será armazenado como uma resposta opaca, ou seja, seu código não poderá ver nem modificar os conteúdos ou 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.

Como depurar o armazenamento em cache

Muitos navegadores oferecem uma maneira de depurar o conteúdo do armazenamento em cache na guia DevTools Application. Lá, você pode ver 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