API кэша: краткое руководство

Узнайте, как использовать Cache API, чтобы сделать данные вашего приложения доступными в автономном режиме.

Cache API — это система хранения и получения сетевых запросов и соответствующих им ответов. Это могут быть регулярные запросы и ответы, созданные в ходе работы вашего приложения, или они могут быть созданы исключительно с целью хранения данных для последующего использования.

API Cache был создан, чтобы позволить работникам службы кэшировать сетевые запросы, чтобы они могли предоставлять быстрые ответы независимо от скорости или доступности сети. Однако API также можно использовать в качестве общего механизма хранения.

Где это доступно?

API Cache доступен во всех современных браузерах . Он предоставляется через свойство глобальных caches , поэтому вы можете проверить наличие API с помощью простого обнаружения функции:

const cacheAvailable = 'caches' in self;

Поддержка браузера

  • Хром: 40.
  • Край: 16.
  • Фаерфокс: 41.
  • Сафари: 11.1.

Источник

Доступ к 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.