CacheStorage

Baseline 已广泛支持

此功能已成熟,可跨多种设备和浏览器版本工作。它自 ⁨2018 年 4 月⁩ 起已在所有浏览器中可用。

安全上下文: 此功能仅在安全上下文(HTTPS)中可用,且支持此功能的浏览器数量有限。

注意:此功能在 Web Workers 中可用。

CacheStorage 接口代表 Cache 对象的存储。

该接口

  • 提供了一个主目录,其中包含所有命名的缓存,这些缓存可以由 ServiceWorker 或其他类型的 worker 或 window 作用域访问(您不限于仅将它与 Service Worker 一起使用)。
  • 维护一个从字符串名称到相应的 Cache 对象的映射。

使用 CacheStorage.open() 来获取一个 Cache 实例。

使用 CacheStorage.match() 来检查给定的 Request 是否是 CacheStorage 对象跟踪的任何 Cache 对象中的一个键。

您可以通过窗口中的 Window.caches 属性或在 worker 中的 WorkerGlobalScope.caches 属性来访问 CacheStorage

注意: CacheStorage 始终会在不受信任的源(即未使用的 HTTPS 的源)上因 SecurityError 而拒绝(尽管此定义将来可能会变得更复杂)。在 Firefox 上进行测试时,您可以通过在 Firefox DevTools 的选项/齿轮菜单中选中“在 HTTP 上启用 Service Worker(当工具箱打开时)”选项来绕过此限制。此外,由于 CacheStorage 需要文件系统访问,因此在 Firefox 的私有模式下可能不可用。

注意: CacheStorage.match() 是一个便捷方法。通过从 CacheStorage.keys() 返回缓存名称的数组,使用 CacheStorage.open() 打开每个缓存,并使用 Cache.match() 匹配您想要的缓存,可以实现匹配缓存条目的等效功能。

实例方法

CacheStorage.match()

检查给定的 Request 是否是 CacheStorage 对象跟踪的任何 Cache 对象中的一个键,并返回一个解析为该匹配项的 Promise

CacheStorage.has()

返回一个 Promise,该 Promise 解析为 true,如果存在匹配 cacheNameCache 对象。

CacheStorage.open()

返回一个 Promise,该 Promise 解析为匹配 cacheNameCache 对象(如果尚不存在,则创建一个新的缓存)。

CacheStorage.delete()

查找匹配 cacheNameCache 对象,如果找到,则删除该 Cache 对象并返回一个解析为 truePromise。如果未找到 Cache 对象,则解析为 false

CacheStorage.keys()

返回一个 Promise,它将解析为一个数组,其中包含对应于 CacheStorage 跟踪的所有命名 Cache 对象的字符串。使用此方法可以遍历所有 Cache 对象列表。

示例

此代码段摘自 MDN 的 简单 Service Worker 示例(请参阅 在线运行的简单 Service Worker)。此 Service Worker 脚本等待 install 事件触发,然后运行 waitUntil 来处理应用程序的安装过程。这包括调用 CacheStorage.open 来创建一个新缓存,然后使用 Cache.addAll 将一系列资源添加到其中。

在第二个代码块中,我们等待 FetchEvent 事件触发。我们像这样构造一个自定义响应:

  1. 检查是否在 CacheStorage 中找到请求的匹配项。如果找到,则提供该匹配项。
  2. 如果未找到,则从网络获取请求,然后打开第一个块中创建的缓存,并使用 Cache.put 将请求的克隆添加到其中(cache.put(event.request, response.clone()))。
  3. 如果失败(例如,因为网络中断),则返回一个备用响应。

最后,使用 FetchEvent.respondWith 返回最终的自定义响应。

js
self.addEventListener("install", (event) => {
  event.waitUntil(
    caches
      .open("v1")
      .then((cache) =>
        cache.addAll([
          "/",
          "/index.html",
          "/style.css",
          "/app.js",
          "/image-list.js",
          "/star-wars-logo.jpg",
          "/gallery/bountyHunters.jpg",
          "/gallery/myLittleVader.jpg",
          "/gallery/snowTroopers.jpg",
        ]),
      ),
  );
});

self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      // caches.match() always resolves
      // but in case of success response will have value
      if (response !== undefined) {
        return response;
      }
      return fetch(event.request)
        .then((response) => {
          // response may be used only once
          // we need to save clone to put one copy in cache
          // and serve second one
          let responseClone = response.clone();

          caches
            .open("v1")
            .then((cache) => cache.put(event.request, responseClone));
          return response;
        })
        .catch(() => caches.match("/gallery/myLittleVader.jpg"));
    }),
  );
});

此代码段展示了如何在 Service Worker 上下文之外使用该 API,并使用 await 运算符使代码更易读。

js
// Try to get data from the cache, but fall back to fetching it live.
async function getData() {
  const cacheVersion = 1;
  const cacheName = `myapp-${cacheVersion}`;
  const url = "https://jsonplaceholder.typicode.com/todos/1";
  let cachedData = await getCachedData(cacheName, url);

  if (cachedData) {
    console.log("Retrieved cached data");
    return cachedData;
  }

  console.log("Fetching fresh data");

  const cacheStorage = await caches.open(cacheName);
  await cacheStorage.add(url);
  cachedData = await getCachedData(cacheName, url);
  await deleteOldCaches(cacheName);

  return cachedData;
}

// Get data from the cache.
async function getCachedData(cacheName, url) {
  const cacheStorage = await caches.open(cacheName);
  const cachedResponse = await cacheStorage.match(url);

  if (!cachedResponse || !cachedResponse.ok) {
    return false;
  }

  return await cachedResponse.json();
}

// Delete any old caches to respect user's disk space.
async function deleteOldCaches(currentCache) {
  const keys = await caches.keys();

  for (const key of keys) {
    const isOurCache = key.startsWith("myapp-");
    if (currentCache === key || !isOurCache) {
      continue;
    }
    caches.delete(key);
  }
}

try {
  const data = await getData();
  console.log({ data });
} catch (error) {
  console.error({ error });
}

规范

规范
Service Workers
# cachestorage-interface

浏览器兼容性

另见