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 e as respostas correspondentes. Podem ser solicitações regulares e as respostas criadas durante a execução do aplicativo, ou podem ser criados exclusivamente com o propósito de armazenar dados para uso posterior.

A API Cache foi criada para permitir que os service workers armazenem em cache as solicitações de rede para que possam fornecer respostas rápidas, independentemente da velocidade da rede ou disponibilidade. 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. É exposto pela propriedade caches global, para que você possa testar a presença de a API com uma detecção de recursos simples:

const cacheAvailable = 'caches' in self;

Compatibilidade com navegadores

  • Chrome: 40.
  • Borda: 16.
  • Firefox: 41.
  • Safari: 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 Request e Objetos Response, que representam solicitações e respostas HTTP, respectivamente. No entanto, as solicitações e respostas podem conter qualquer tipo de dados que podem ser transferidos por HTTP.

Quantos dados podem ser armazenados?

Resumindo, muitos, pelo menos algumas centenas de megabytes e potencialmente centenas de gigabytes ou mais. As implementações dos navegadores variam, mas a quantidade de armazenamento disponível geralmente se baseia na quantidade de armazenamento o dispositivo.

Como criar e abrir um cache

Para abrir um cache, use o método caches.open(name), transmitindo o nome do cache como parâmetro único. Se o cache nomeado não existe, é criados. 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, seja um Request. ou um URL (string). Ele faz uma solicitação à rede e armazena a resposta no cache. Se o a busca falhar, ou se o código de status da resposta não estiver no intervalo 200, nada será armazenado, e o Promise será rejeitado. Os dados de origem cruzada solicitações fora do 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(). Funciona de forma semelhante a add(), mas usa uma matriz de objetos Request ou URLs (strings). Isso funciona de forma semelhante chamando cache.add para cada solicitação individual, exceto pelo fato de que Promise será rejeitada se uma única solicitação não estiver 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 recuperando.

cache.put

Por fim, há cache.put(), que permite armazenar uma resposta da rede ou crie e armazene seus próprios Response. São necessárias duas parâmetros. O primeiro pode ser um objeto Request ou um URL (string). O segundo precisa ser um Response da rede ou gerado pelo seu o código-fonte.

// 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 permitem armazenar respostas não CORS ou outras respostas em que o status da resposta não está no intervalo 200. Ele substituirá para a 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 do objeto 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 resolve 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 tiver sido criado com um Blob, então esse novo Blob terá o mesmo não é válido. Caso contrário, o Content-Type do Response é 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 para analisá-lo como JSON. Retorna o objeto resultante ou gera uma TypeError se a string não puder 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 FormData ou gera uma TypeError se os dados não puderem 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 uma Request chamando new Request(request) A função retorna um Promise que é resolvido como um Response se uma entrada correspondente for encontrada; caso contrário, undefined.

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

Você pode ignorar alguns ou todos esses itens passando 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 houver correspondência de mais de uma solicitação em cache, aquela que foi criada primeiro será retornados. Se quiser recuperar todas as respostas correspondentes, você poderá usar 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, você pode pesquisar em 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 fornece uma maneira de pesquisar solicitações ou respostas exceto para entradas correspondentes a um objeto Response. No entanto, é possível implementar sua própria pesquisa usando filtros ou criando um índice.

Filtragem

Uma forma de implementar sua própria pesquisa é iterar em todas as entradas e e filtre pelos que você quer. Digamos que você queira encontrar todos itens com 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 é lento quando você pesquisa 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 podem ser pesquisadas e armazenar o índice no IndexedDB. Como este é o tipo de operação para a qual o IndexedDB foi projetado, tem um desempenho muito melhor um grande número de entradas.

Se você armazenar o URL do Request com as propriedades pesquisáveis você poderá recuperar facilmente a entrada correta no 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 recebe mesmo objeto de opções de cache.match, que permite excluir várias Request/Response para o 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 uma Promise, que é resolvido como true se o cache existia e foi excluído; ou Caso contrário, false.

Obrigado

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