API Cache: um guia rápido

Saiba como usar a API Cache para disponibilizar os dados do seu aplicativo off-line.

A API Cache é um sistema para armazenar e recuperar solicitações de rede e as respostas correspondentes. Podem ser solicitações e respostas regulares criadas durante a execução do aplicativo ou podem ser criadas exclusivamente com o propósito de armazenar dados para uso posterior.

A API Cache foi criada para permitir que os service workers armazenem solicitações de rede em cache para que possam fornecer respostas rápidas, independentemente da velocidade ou disponibilidade da rede. No entanto, a API também pode ser usada como um mecanismo geral de armazenamento.

Onde ele está disponível?

A API Cache está disponível em todos os navegadores modernos. Ela é exposta pela propriedade caches global para que você possa testar a presença da API com uma detecção de recursos simples:

const cacheAvailable = 'caches' in self;

Compatibilidade com navegadores

  • 40
  • 16
  • 41
  • 11.1

Origem

A API Cache pode ser acessada em uma janela, iframe, worker ou service worker.

O que pode ser armazenado

Os caches armazenam apenas pares de objetos Request e Response, representando solicitações e respostas HTTP, respectivamente. No entanto, as solicitações e respostas podem conter qualquer tipo de dados transferidos por HTTP.

Quantos dados podem ser armazenados?

Resumindo, muitos, pelo menos algumas centenas de megabytes e possivelmente centenas de gigabytes ou mais. As implementações do navegador variam, mas a quantidade de armazenamento disponível geralmente depende do armazenamento disponível no dispositivo.

Como criar e abrir um cache

Para abrir um cache, use o método caches.open(name), transmitindo o nome do cache como o parâmetro único. Se o cache nomeado não existir, ele será criado. Esse método retorna um Promise que é resolvido com o objeto Cache.

const cache = await caches.open('my-cache');
// do something with cache...

Como adicionar a um cache

Há três maneiras de adicionar um item a um cache: add, addAll e put. Todos os três métodos retornam um Promise.

cache.add

Primeiro, há cache.add(). Ele utiliza um parâmetro, um Request ou um URL (string). Ele faz uma solicitação à rede e armazena a resposta no cache. Se a busca falhar ou se o código de status da resposta não estiver no intervalo 200, nada será armazenada, e o Promise será rejeitado. Observe que as solicitações de origem cruzada que não estão no modo CORS não podem ser armazenadas porque retornam um status de 0. Essas solicitações só podem ser armazenadas com put.

// Retreive data.json from the server and store the response.
cache.add(new Request('/data.json'));

// Retreive data.json from the server and store the response.
cache.add('/data.json');

cache.addAll

Em seguida, temos cache.addAll(). Ele funciona de maneira semelhante a add(), mas usa uma matriz de objetos Request ou URLs (strings). Isso funciona de maneira semelhante a chamar cache.add para cada solicitação individual, com a exceção de que a Promise será rejeitada se uma única solicitação não for armazenada em cache.

const urls = ['/weather/today.json', '/weather/tomorrow.json'];
cache.addAll(urls);

Em cada um desses casos, uma nova entrada substitui qualquer entrada correspondente. Isso usa as mesmas regras de correspondência descritas na seção sobre retrieving.

cache.put

Por fim, há o cache.put(), que permite armazenar uma resposta da rede ou criar e armazenar sua própria Response. Ele usa dois parâmetros. O primeiro pode ser um objeto Request ou um URL (string). O segundo precisa ser um Response da rede ou gerado pelo código.

// Retrieve data.json from the server and store the response.
cache.put('/data.json');

// Create a new entry for test.json and store the newly created response.
cache.put('/test.json', new Response('{"foo": "bar"}'));

// Retrieve data.json from the 3rd party site and store the response.
cache.put('https://example.com/data.json');

O método put() é mais permissivo do que add() ou addAll() e permite armazenar respostas que não sejam CORS ou outras respostas em que o código de status da resposta não esteja no intervalo 200. Ele vai substituir as respostas anteriores da mesma solicitação.

Como criar objetos de solicitação

Crie o objeto Request usando um URL para o item que está sendo armazenado:

const request = new Request('/my-data-store/item-id');

Como trabalhar com objetos de resposta

O construtor de objetos Response aceita muitos tipos de dados, incluindo Blobs, ArrayBuffers, objetos FormData e strings.

const imageBlob = new Blob([data], {type: 'image/jpeg'});
const imageResponse = new Response(imageBlob);
const stringResponse = new Response('Hello world');

Para definir o tipo MIME de uma Response, defina o cabeçalho adequado.

  const options = {
    headers: {
      'Content-Type': 'application/json'
    }
  }
  const jsonResponse = new Response('{}', options);

Se você recuperou um Response e quer acessar o corpo dele, há vários métodos auxiliares que podem ser usados. Cada um retorna um Promise que é resolvido com um valor de um tipo diferente.

Método Descrição
arrayBuffer Retorna um ArrayBuffer contendo o corpo, serializado para bytes.
blob Retorna um Blob. Se o Response foi criado com um Blob, esse novo Blob terá o mesmo tipo. Caso contrário, o Content-Type do Response será usado.
text Interpreta os bytes do corpo como uma string codificada em UTF-8.
json Interpreta os bytes do corpo como uma string codificada em UTF-8 e tenta analisá-lo como JSON. Retorna o objeto resultante ou gera um TypeError se a string não pode ser analisada como JSON.
formData Interpreta os bytes do corpo como um formulário HTML, codificado como multipart/form-data ou application/x-www-form-urlencoded. Retorna um objeto FormData ou gera um TypeError se os dados não podem ser analisados.
body Retorna um ReadableStream para os dados do corpo.

Por exemplo:

const response = new Response('Hello world');
const buffer = await response.arrayBuffer();
console.log(new Uint8Array(buffer));
// Uint8Array(11) [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]

Como recuperar de um cache

Para encontrar um item em um cache, use o método match.

const response = await cache.match(request);
console.log(request, response);

Se request for uma string, o navegador a converterá em um Request chamando new Request(request). A função retorna um Promise que é resolvido como Response se uma entrada correspondente é encontrada. Caso contrário, retorna undefined.

Para determinar se dois Requests são correspondentes, o navegador usa mais do que apenas o URL. Duas solicitações serão consideradas diferentes se tiverem strings de consulta, cabeçalhos Vary ou métodos HTTP diferentes (GET, POST, PUT etc.).

Você pode ignorar alguns ou todos esses itens ao transmitir um objeto de opções como um segundo parâmetro.

const options = {
  ignoreSearch: true,
  ignoreMethod: true,
  ignoreVary: true
};

const response = await cache.match(request, options);
// do something with the response

Se mais de uma solicitação em cache corresponder, a que foi criada primeiro será retornada. Se você quiser recuperar todas as respostas correspondentes, use cache.matchAll().

const options = {
  ignoreSearch: true,
  ignoreMethod: true,
  ignoreVary: true
};

const responses = await cache.matchAll(request, options);
console.log(`There are ${responses.length} matching responses.`);

Como atalho, é possível pesquisar todos os caches de uma só vez usando caches.match() em vez de chamar cache.match() para cada cache.

Pesquisando…

A API Cache não oferece uma maneira de pesquisar solicitações ou respostas, exceto para entradas correspondentes a um objeto Response. No entanto, você pode implementar sua própria pesquisa usando filtragem ou criando um índice.

Filtragem

Uma maneira de implementar sua própria pesquisa é iterar todas as entradas e filtrar as que você quer. Digamos que você queira encontrar todos os itens que têm URLs terminados em .png.

async function findImages() {
  // Get a list of all of the caches for this origin
  const cacheNames = await caches.keys();
  const result = [];

  for (const name of cacheNames) {
    // Open the cache
    const cache = await caches.open(name);

    // Get a list of entries. Each item is a Request object
    for (const request of await cache.keys()) {
      // If the request URL matches, add the response to the result
      if (request.url.endsWith('.png')) {
        result.push(await cache.match(request));
      }
    }
  }

  return result;
}

Dessa forma, é possível usar qualquer propriedade dos objetos Request e Response para filtrar as entradas. Esse processo será lento se você pesquisar em grandes conjuntos de dados.

Como criar um índice

A outra maneira de implementar sua própria pesquisa é manter um índice separado de entradas que possam ser pesquisadas e armazenar o índice no IndexedDB. Como esse é o tipo de operação para o qual o IndexedDB foi projetado, ele tem um desempenho muito melhor com um grande número de entradas.

Se você armazenar o URL do Request junto com as propriedades pesquisáveis, poderá recuperar facilmente a entrada correta do cache depois de fazer a pesquisa.

Como excluir um item

Para excluir um item de um cache:

cache.delete(request);

Em que a solicitação pode ser Request ou uma string de URL. Esse método também usa o mesmo objeto de opções que cache.match, o que permite excluir vários pares de Request/Response do mesmo URL.

cache.delete('/example/file.txt', {ignoreVary: true, ignoreSearch: true});

Como excluir um cache

Para excluir um cache, chame caches.delete(name). Essa função retorna um Promise que é resolvido para true se o cache existiu e tiver sido excluído, ou false caso contrário.

Obrigado.

Agradecemos a Mat Scales, que escreveu a versão original deste artigo, que apareceu no WebFundamentals.