Cache API:快速指南

了解如何使用 Cache API 使您的应用程序数据可脱机使用。

Cache API 是一种用于存储和检索网络请求及其相应响应的系统。这些可能是运行应用程序过程中创建的常规请求和响应,也可能是专门为存储数据以供以后使用而创建的请求和响应。

创建 Cache API 的目的是使 Service Worker 能够缓存网络请求,以便它们可以提供快速响应,而无需考虑网络速度或可用性。但是,该 API 也可以用作通用的存储机制。

它在何处可用?

Cache API 在所有现代浏览器中均可用。它通过全局 caches 属性公开,因此您可以使用简单的功能检测来测试 API 的存在

const cacheAvailable = 'caches' in self;

浏览器支持

  • Chrome: 40.
  • Edge: 16.
  • Firefox: 41.
  • Safari: 11.1.

来源

可以从窗口、iframe、Worker 或 Service Worker 访问 Cache API。

可以存储什么

缓存仅存储 RequestResponse 对象的对,分别代表 HTTP 请求和响应。但是,请求和响应可以包含可以通过 HTTP 传输的任何类型的数据。

可以存储多少?

简而言之,很多,至少几百兆字节,并且可能达到数百吉字节或更多。浏览器实现各不相同,但可用存储量通常基于设备上可用的存储量。

创建和打开缓存

要打开缓存,请使用 caches.open(name) 方法,并将缓存名称作为单个参数传递。如果指定的缓存不存在,则会创建它。此方法返回一个 Promise,该 Promise 使用 Cache 对象进行解析。

const cache = await caches.open('my-cache');
// do something with cache...

添加到缓存

有三种方法可以将项目添加到缓存 - addaddAllput。这三种方法都返回一个 Promise

cache.add

首先是 cache.add()。它接受一个参数,可以是 Request 或 URL (string)。它向网络发出请求并将响应存储在缓存中。如果提取失败,或者响应的状态代码不在 200 范围内,则不会存储任何内容,并且 Promise 将拒绝。请注意,非 CORS 模式下的跨域请求无法存储,因为它们返回 status0。此类请求只能使用 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) 的数组。这与为每个单独的请求调用 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 创建 Request 对象

const request = new Request('/my-data-store/item-id');

使用 Response 对象

Response 对象构造函数接受多种类型的数据,包括 BlobArrayBufferFormData 对象和字符串。

const imageBlob = new Blob([data], {type: 'image/jpeg'});
const imageResponse = new Response(imageBlob);
const stringResponse = new Response('Hello world');

您可以通过设置适当的标头来设置 Response 的 MIME 类型。

  const options = {
    headers: {
      'Content-Type': 'application/json'
    }
  }
  const jsonResponse = new Response('{}', options);

如果您检索了 Response 并希望访问其正文,则可以使用几种辅助方法。每种方法都返回一个 Promise,该 Promise 使用不同类型的值进行解析。

方法 描述
arrayBuffer 返回一个 ArrayBuffer,其中包含序列化为字节的正文。
blob 返回一个 Blob。如果使用 Blob 创建了 Response,则此新 Blob 具有相同的类型。否则,将使用 ResponseContent-Type
text 将正文的字节解释为 UTF-8 编码的字符串。
json 将正文的字节解释为 UTF-8 编码的字符串,然后尝试将其解析为 JSON。返回结果对象;如果字符串无法解析为 JSON,则抛出 TypeError
formData 将正文的字节解释为 HTML 表单,编码为 multipart/form-dataapplication/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 是字符串,则浏览器通过调用 new Request(request) 将其转换为 Request。该函数返回一个 Promise,如果找到匹配的条目,则解析为 Response,否则解析为 undefined

为了确定两个 Request 是否匹配,浏览器使用的不仅仅是 URL。如果两个请求具有不同的查询字符串、Vary 标头或 HTTP 方法(GETPOSTPUT 等),则认为它们是不同的。

您可以通过传递选项对象作为第二个参数来忽略其中一些或全部内容。

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() 来一次搜索所有缓存。

搜索

Cache 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;
}

这样,您可以使用 RequestResponse 对象的任何属性来筛选条目。请注意,如果您搜索大量数据集,这会很慢。

创建索引

实现您自己的搜索的另一种方法是维护一个单独的可搜索条目索引,并将该索引存储在 IndexedDB 中。由于这正是 IndexedDB 旨在执行的操作类型,因此它在处理大量条目时具有更好的性能。

如果您将 Request 的 URL 与可搜索属性一起存储,则可以在完成搜索后轻松检索正确的缓存条目。

删除项目

要从缓存中删除项目

cache.delete(request);

其中 request 可以是 Request 或 URL 字符串。此方法还采用与 cache.match 相同的 options 对象,这使您可以为同一 URL 删除多个 Request/Response 对。

cache.delete('/example/file.txt', {ignoreVary: true, ignoreSearch: true});

删除缓存

要删除缓存,请调用 caches.delete(name)。此函数返回一个 Promise,如果缓存存在且已删除,则解析为 true,否则解析为 false

感谢

感谢 Mat Scales 撰写了本文的原始版本,该版本最初出现在 WebFundamentals 上。