Узнайте, как использовать API кэширования, чтобы сделать данные вашего приложения доступными в автономном режиме.
API кэша — это система хранения и извлечения сетевых запросов и соответствующих им ответов. Это могут быть обычные запросы и ответы, созданные в ходе работы вашего приложения, или они могут быть созданы исключительно с целью хранения данных для последующего использования.
API кэширования было создано для того, чтобы позволить сервис-воркерам кэшировать сетевые запросы, чтобы они могли предоставлять быстрые ответы, независимо от скорости или доступности сети. Однако API также может использоваться как общий механизм хранения.
Где это доступно?
API кэша доступен во всех современных браузерах . Он отображается через свойство global caches
, поэтому вы можете проверить наличие API с помощью простого обнаружения функции:
const cacheAvailable = 'caches' in self;
Доступ к 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()
.
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 кэша не предоставляет способа поиска запросов или ответов, за исключением сопоставления записей с объектом 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 может быть Request
или строкой URL. Этот метод также принимает тот же объект options, что и cache.match
, что позволяет удалять несколько пар Request
/ Response
для одного и того же URL.
cache.delete('/example/file.txt', {ignoreVary: true, ignoreSearch: true});
Удаление кэша
Чтобы удалить кэш, вызовите caches.delete(name)
. Эта функция возвращает Promise
, который разрешается как true
если кэш существовал и был удален, или false
в противном случае.
Спасибо
Выражаю благодарность Мэту Скейлсу, написавшему оригинальную версию этой статьи, которая впервые появилась на WebFundamentals.