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.
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".