Cache

Baseline 已广泛支持

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

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

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

Cache 接口提供了一种持久化存储机制,用于存储 Request / Response 对象对,这些对象被缓存在长寿命内存中。Cache 对象的生命周期取决于浏览器,但单个源的脚本通常可以依赖于先前填充的 Cache 对象。请注意,Cache 接口既暴露给窗口作用域,也暴露给 worker。您不必将其与 service worker 结合使用,即使它是在 service worker 规范中定义的。

一个源可以有多个命名 Cache 对象。您负责实现脚本(例如,在 ServiceWorker 中)如何处理 Cache 更新。Cache 中的项目不会自动更新,除非明确请求;它们不会过期,除非被删除。使用 CacheStorage.open() 来打开特定的命名 Cache 对象,然后调用任何 Cache 方法来维护 Cache

您还负责定期清理缓存条目。每个浏览器对给定源可以使用的缓存存储量都有硬性限制。可以通过 StorageManager.estimate() 方法估算 Cache 配额使用情况。浏览器会尽最大努力管理磁盘空间,但它可能会删除源的 Cache 存储。浏览器通常会删除一个源的所有数据,或者不删除任何数据。请确保通过名称对缓存进行版本化,并仅使用可以安全操作它们的脚本版本中的缓存。有关更多信息,请参阅 删除旧缓存

注意: 键匹配算法取决于值的 VARY 头。因此,匹配新键需要查看 Cache 对象中条目的键和值。

注意: 缓存 API 不遵守 HTTP 缓存头。

实例方法

Cache.match()

返回一个 Promise,该 Promise 解析为 Cache 对象中第一个匹配请求的响应。

Cache.matchAll()

返回一个 Promise,该 Promise 解析为 Cache 对象中所有匹配响应的数组。

Cache.add()

接收一个 URL,检索它,并将结果响应对象添加到给定的缓存中。这在功能上等同于调用 fetch(),然后使用 put() 将结果添加到缓存。

Cache.addAll()

接收一个 URL 数组,检索它们,并将结果响应对象添加到给定的缓存中。

Cache.put()

接收请求及其响应,并将它们添加到给定的缓存中。

Cache.delete()

查找键为请求的 Cache 条目,返回一个 Promise,如果找到并删除了匹配的 Cache 条目,则该 Promise 解析为 true。如果未找到 Cache 条目,则 Promise 解析为 false

Cache.keys()

返回一个 Promise,该 Promise 解析为 Cache 键的数组。

示例

此代码片段来自 service worker 选择性缓存示例。(请参阅 选择性缓存实时演示) 代码使用 CacheStorage.open() 来打开任何 Content-Type 头以 font/ 开头的 Cache 对象。

然后,代码使用 Cache.match() 来检查缓存中是否已有匹配的字体,如果有,则返回它。如果不存在匹配的字体,代码将从网络获取字体,并使用 Cache.put() 将获取的资源缓存起来。

代码处理从 fetch() 操作抛出的异常。请注意,HTTP 错误响应(例如 404)不会引发异常。它将返回一个具有相应错误代码的正常响应对象。

代码片段还展示了 service worker 使用的缓存版本化的最佳实践。虽然在此示例中只有一个缓存,但相同的方法可用于多个缓存。它将缓存的简短标识符映射到特定的、版本化的缓存名称。代码还会删除所有未在 CURRENT_CACHES 中命名的缓存。

在代码示例中,cachesServiceWorkerGlobalScope 的一个属性。它包含 CacheStorage 对象,通过它可以访问 CacheStorage 接口。

注意: 在 Chrome 中,访问 chrome://inspect/#service-workers 并点击已注册的 service worker 下方的“inspect”链接,可以查看 service-worker.js 脚本执行的各种操作的日志记录语句。

js
const CACHE_VERSION = 1;
const CURRENT_CACHES = {
  font: `font-cache-v${CACHE_VERSION}`,
};

self.addEventListener("activate", (event) => {
  // Delete all caches that aren't named in CURRENT_CACHES.
  // While there is only one cache in this example, the same logic
  // will handle the case where there are multiple versioned caches.
  const expectedCacheNamesSet = new Set(Object.values(CURRENT_CACHES));
  event.waitUntil(
    caches.keys().then((cacheNames) =>
      Promise.all(
        cacheNames.map((cacheName) => {
          if (!expectedCacheNamesSet.has(cacheName)) {
            // If this cache name isn't present in the set of
            // "expected" cache names, then delete it.
            console.log("Deleting out of date cache:", cacheName);
            return caches.delete(cacheName);
          }
          return undefined;
        }),
      ),
    ),
  );
});

self.addEventListener("fetch", (event) => {
  console.log("Handling fetch event for", event.request.url);

  event.respondWith(
    caches
      .open(CURRENT_CACHES.font)
      .then((cache) => cache.match(event.request))
      .then((response) => {
        if (response) {
          // If there is an entry in the cache for event.request,
          // then response will be defined and we can just return it.
          // Note that in this example, only font resources are cached.
          console.log(" Found response in cache:", response);

          return response;
        }

        // Otherwise, if there is no entry in the cache for event.request,
        // response will be undefined, and we need to fetch() the resource.
        console.log(
          " No response for %s found in cache. About to fetch " +
            "from network…",
          event.request.url,
        );

        // We call .clone() on the request since we might use it
        // in a call to cache.put() later on.
        // Both fetch() and cache.put() "consume" the request,
        // so we need to make a copy.
        // (see https://mdn.org.cn/en-US/docs/Web/API/Request/clone)
        return fetch(event.request.clone()).then((response) => {
          console.log(
            "  Response for %s from network is: %O",
            event.request.url,
            response,
          );

          if (
            response.status < 400 &&
            response.headers.has("content-type") &&
            response.headers.get("content-type").match(/^font\//i)
          ) {
            // This avoids caching responses that we know are errors
            // (i.e. HTTP status code of 4xx or 5xx).
            // We also only want to cache responses that correspond
            // to fonts, i.e. have a Content-Type response header that
            // starts with "font/".
            // Note that for opaque filtered responses
            // https://fetch.spec.whatwg.org/#concept-filtered-response-opaque
            // we can't access to the response headers, so this check will
            // always fail and the font won't be cached.
            // All of the Google Web Fonts are served from a domain that
            // supports CORS, so that isn't an issue here.
            // It is something to keep in mind if you're attempting
            // to cache other resources from a cross-origin
            // domain that doesn't support CORS, though!
            console.log("  Caching the response to", event.request.url);
            // We call .clone() on the response to save a copy of it
            // to the cache. By doing so, we get to keep the original
            // response object which we will return back to the controlled
            // page.
            // https://mdn.org.cn/en-US/docs/Web/API/Request/clone
            cache.put(event.request, response.clone());
          } else {
            console.log("  Not caching the response to", event.request.url);
          }

          // Return the original response object, which will be used to
          // fulfill the resource request.
          return response;
        });
      })
      .catch((error) => {
        // This catch() will handle exceptions that arise from the match()
        // or fetch() operations.
        // Note that a HTTP error response (e.g. 404) will NOT trigger
        // an exception.
        // It will return a normal response object that has the appropriate
        // error code set.
        console.error("  Error in fetch handler:", error);

        throw error;
      }),
  );
});

Cookie 和 Cache 对象

Fetch API 要求在从 fetch() 返回 Response 对象之前,删除 Set-Cookie 头。因此,存储在 Cache 中的 Response 将不包含 Set-Cookie 头,因此不会导致任何 Cookie 被存储。

规范

规范
Service Workers
# cache-interface

浏览器兼容性

另见