Saiba como usar a API Cache para disponibilizar os dados do aplicativo off-line.
A API Cache é um sistema para armazenar e extrair solicitações de rede e as respostas correspondentes. Elas podem ser solicitações e respostas regulares criadas durante a execução do aplicativo ou criada apenas para 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 eles possam fornecer respostas rápidas, independentemente da velocidade ou disponibilidade da rede. No entanto, a API também pode ser usada como um mecanismo de armazenamento geral.
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;
A API Cache pode ser acessada de uma janela, um iframe, um worker ou um 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
que possam ser transferidos por HTTP.
Quantos dados podem ser armazenados?
Em resumo, muito, pelo menos algumas centenas de megabytes e, potencialmente, centenas de gigabytes ou mais. As implementações do navegador variam, mas a quantidade de armazenamento disponível geralmente é baseada no 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 único parâmetro. 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 usa um parâmetro, um Request
ou um URL (string
). Ele faz uma solicitação para a 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á armazenado e a Promise
será rejeitada. As solicitações entre origens
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 pega uma
matriz de objetos Request
ou URLs (string
s). Isso funciona de maneira semelhante a
chamada de cache.add
para cada solicitação individual, exceto que o Promise
rejeita se uma única solicitação não estiver 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 recuperação.
cache.put
Por fim, há cache.put()
, que permite armazenar uma resposta
da rede ou criar e armazenar seu próprio 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 seu
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 são CORS ou outras respostas em que o código de status
não está no intervalo 200. Ele vai substituir todas as respostas
anteriores para a mesma solicitação.
Como criar objetos de solicitação
Crie o objeto Request
usando um URL para a coisa armazenada:
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
Blob
s, ArrayBuffer
s, objetos FormData
e strings.
const imageBlob = new Blob([data], {type: 'image/jpeg'});
const imageResponse = new Response(imageBlob);
const stringResponse = new Response('Hello world');
É possível definir o tipo MIME de um Response
definindo o cabeçalho apropriado.
const options = {
headers: {
'Content-Type': 'application/json'
}
}
const jsonResponse = new Response('{}', options);
Se você extrair um Response
e quiser acessar o corpo dele, use
vários métodos auxiliares. 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 em
bytes.
|
blob |
Retorna um Blob . Se o Response foi criado
com um Blob , esse novo Blob terá o mesmo
type. 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á-la 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 objeto
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 dados 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
um Response
se uma entrada correspondente for encontrada ou undefined
, caso contrário.
Para determinar se dois Requests
correspondem, o navegador usa mais do que apenas o URL. Duas
solicitações são consideradas diferentes se tiverem strings de consulta,
cabeçalhos Vary
ou métodos HTTP (GET
, POST
, PUT
etc.) diferentes.
É possível ignorar algumas ou todas essas coisas transmitindo 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 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 a filtragem ou criando um índice.
Filtragem
Uma maneira de implementar sua própria pesquisa é iterar sobre todas as entradas e
filtrar para as que você quiser. Digamos que você queira encontrar todos
os itens com URLs que terminam 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, você pode usar qualquer propriedade dos objetos Request
e Response
para
filtrar as entradas. Isso 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 podem 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 uma 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 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 um
Promise
que é resolvido como true
se o cache existir e for excluído ou
false
, caso contrário.
Obrigado
Agradecemos a Mat Scales, que escreveu a versão original deste artigo, que apareceu pela primeira vez no WebFundamentals.