Узнайте, как использовать Cache API, чтобы сделать данные вашего приложения доступными в автономном режиме.
Cache API — это система хранения и получения сетевых запросов и соответствующих им ответов. Это могут быть регулярные запросы и ответы, созданные в ходе работы вашего приложения, или они могут быть созданы исключительно с целью хранения данных для последующего использования.
API Cache был создан, чтобы позволить работникам службы кэшировать сетевые запросы, чтобы они могли предоставлять быстрые ответы независимо от скорости или доступности сети. Однако API также можно использовать в качестве общего механизма хранения.
Где это доступно?
API Cache доступен во всех современных браузерах . Он предоставляется через свойство глобальных caches
, поэтому вы можете проверить наличие API с помощью простого обнаружения функции:
const cacheAvailable = 'caches' in self;
Доступ к Cache API можно получить из окна, iframe, работника или сервис-воркера.
Что можно хранить
В кешах хранятся только пары объектов Request
и Response
, представляющие HTTP-запросы и ответы соответственно. Однако запросы и ответы могут содержать любые данные, которые можно передавать по HTTP.
Сколько можно хранить?
Короче говоря, очень много , как минимум пара сотен мегабайт, а потенциально и сотни гигабайт и более. Реализации браузеров различаются, но объем доступного хранилища обычно зависит от объема хранилища, доступного на устройстве.
Создание и открытие кэша
Чтобы открыть кэш, используйте метод caches.open(name)
, передав имя кэша в качестве единственного параметра. Если именованный кэш не существует, он создается. Этот метод возвращает Promise
, которое разрешается с помощью объекта Cache
.
const cache = await caches.open('my-cache');
// do something with cache...
Добавление в кэш
Существует три способа добавить элемент в кэш: add
, addAll
и put
. Все три метода возвращают Promise
.
cache.add
Во-первых, есть cache.add()
. Он принимает один параметр: Request
или URL-адрес ( string
). Он отправляет запрос в сеть и сохраняет ответ в кеше. Если выборка не удалась или код состояния ответа не находится в диапазоне 200, то ничего не сохраняется и Promise
отклоняется. Обратите внимание, что запросы между источниками, не находящиеся в режиме CORS, не могут быть сохранены, поскольку они возвращают status
0
. Такие запросы можно сохранить только с помощью 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
Далее идет cache.addAll()
. Он работает аналогично add()
, но принимает массив объектов Request
или URL-адресов ( string
s). Это работает аналогично вызову cache.add
для каждого отдельного запроса, за исключением того, что Promise
отклоняется, если какой-либо отдельный запрос не кэшируется.
const urls = ['/weather/today.json', '/weather/tomorrow.json'];
cache.addAll(urls);
В каждом из этих случаев новая запись перезаписывает любую соответствующую существующую запись. При этом используются те же правила сопоставления, которые описаны в разделе о получении файлов .
cache.put
Наконец, есть cache.put()
, который позволяет вам хранить либо ответ из сети, либо создавать и сохранять свой собственный Response
. Требуется два параметра. Первым может быть либо объект Request
, либо URL-адрес ( string
). Вторым должен быть Response
либо из сети, либо сгенерированный вашим кодом.
// 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');
Метод put()
является более разрешительным, чем add()
или addAll()
, и позволяет хранить ответы, отличные от CORS, или другие ответы, код состояния ответа которых не находится в диапазоне 200. Он перезапишет все предыдущие ответы на тот же запрос.
Создание объектов запроса
Создайте объект Request
, используя URL-адрес сохраняемого объекта:
const request = new Request('/my-data-store/item-id');
Работа с объектами Response
Конструктор объекта Response
принимает многие типы данных, включая Blob
, ArrayBuffer
, объекты FormData
и строки.
const imageBlob = new Blob([data], {type: 'image/jpeg'});
const imageResponse = new Response(imageBlob);
const stringResponse = new Response('Hello world');
Вы можете установить MIME-тип Response
, установив соответствующий заголовок.
const options = {
headers: {
'Content-Type': 'application/json'
}
}
const jsonResponse = new Response('{}', options);
Если вы получили Response
и хотите получить доступ к его телу, вы можете использовать несколько вспомогательных методов. Каждый возвращает Promise
, которое разрешается со значением другого типа.
Метод | Описание |
---|---|
arrayBuffer | Возвращает ArrayBuffer , содержащий тело, сериализованное в байты. |
blob | Возвращает Blob . Если Response был создан с помощью Blob , этот новый Blob имеет тот же тип. В противном случае используется Content-Type Response . |
text | Интерпретирует байты тела как строку в кодировке UTF-8. |
json | Интерпретирует байты тела как строку в кодировке UTF-8, а затем пытается проанализировать ее как JSON. Возвращает результирующий объект или выдает TypeError если строку невозможно проанализировать как JSON. |
formData | Интерпретирует байты тела как HTML-форму, закодированную либо как multipart/form-data либо как application/x-www-form-urlencoded . Возвращает объект FormData или выдает TypeError , если данные не могут быть проанализированы. |
body | Возвращает ReadableStream для данных тела. |
Например
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]
Получение из кэша
Чтобы найти элемент в кеше, вы можете использовать метод match
.
const response = await cache.match(request);
console.log(request, response);
Если request
представляет собой строку, браузер преобразует ее в Request
, вызывая new Request(request)
. Функция возвращает Promise
, которое преобразуется в Response
, если найдена соответствующая запись, или в противном случае undefined
.
Чтобы определить совпадение двух Requests
, браузер использует не только URL-адрес. Два запроса считаются разными, если они имеют разные строки запроса, заголовки Vary
или методы HTTP ( GET
, POST
, PUT
и т. д.).
Вы можете игнорировать некоторые или все эти вещи, передав объект параметров в качестве второго параметра.
const options = {
ignoreSearch: true,
ignoreMethod: true,
ignoreVary: true
};
const response = await cache.match(request, options);
// do something with the response
Если совпадает более одного кэшированного запроса, возвращается тот, который был создан первым. Если вы хотите получить все совпадающие ответы, вы можете использоватьcache.matchAll 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.`);
В качестве ярлыка вы можете выполнять поиск по всем кешам одновременно, используя caches.match()
вместо вызова cache.match()
для каждого кеша.
Идет поиск
API Cache не предоставляет способа поиска запросов или ответов, за исключением сопоставления записей с объектом Response
. Однако вы можете реализовать собственный поиск, используя фильтрацию или создав индекс.
Фильтрация
Один из способов реализовать собственный поиск — перебрать все записи и отфильтровать те, которые вам нужны. Допустим, вы хотите найти все элементы, URL-адреса которых заканчиваются на .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;
}
Таким образом, вы можете использовать любое свойство объектов Request
и Response
для фильтрации записей. Обратите внимание, что это медленно, если вы ищете большие наборы данных.
Создание индекса
Другой способ реализовать собственный поиск — создать отдельный индекс записей, по которым можно осуществлять поиск, и сохранить этот индекс в IndexedDB. Поскольку это тип операции, для которой была разработана IndexedDB, она имеет гораздо лучшую производительность при большом количестве записей.
Если вы сохраните URL-адрес Request
вместе со свойствами, доступными для поиска, вы сможете легко получить правильную запись в кэше после выполнения поиска.
Удаление элемента
Чтобы удалить элемент из кэша:
cache.delete(request);
Где запрос может быть Request
или строкой URL. Этот метод также принимает тот же объект параметров, что и cache.match
, что позволяет удалять несколько пар Request
/ Response
для одного и того же URL-адреса.
cache.delete('/example/file.txt', {ignoreVary: true, ignoreSearch: true});
Удаление кэша
Чтобы удалить кеш, вызовите caches.delete(name)
. Эта функция возвращает Promise
, которое принимает значение true
, если кэш существовал и был удален, или false
в противном случае.
Спасибо
Спасибо Мэту Скейлсу, написавшему оригинальную версию этой статьи, которая впервые появилась на WebFundamentals.