You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何利用JavaScript Cache API存储缓存日期并解决Headers不可变问题?

解决Chrome中Cache API修改Response Headers报错的问题

问题原因

Chrome严格遵循Fetch API规范,从fetch()返回的Response对象的Headers是**不可变(immutable)**的,直接调用headers.set()会抛出TypeError;而Firefox对该规范的实现更为宽松,允许修改这些Headers,这就是跨浏览器行为不一致的核心原因。

解决方案:创建可修改的新Response对象

要在客户端为缓存的Response添加创建时间,你需要基于原响应创建一个新的Response实例,在新实例中自定义Headers。推荐使用自定义头(而非标准Date头)来避免冲突,比如X-Cache-Creation-Date。

修改后的代码

/** Searches for the corresponding cache for the given request. If found, returns
 *  the cached response. Otherwise, performs the fetch request and adds the response
 *  to cache. Returns the HTTP response.
 */
export async function fetchCachedData(request: Request) {
  const cache = await caches.open(CACHE_NAME);

  // 检查是否已有缓存响应
  const cachedResponse = await cache.match(request);
  if (cachedResponse) {
    console.debug("Using cached response for", request.url);
    return cachedResponse.clone();
  }

  // 获取新响应
  console.debug("Fetching", request.url, "...");
  const originalResponse = await fetchFromAPI(request);
  const responseDate = new Date().getTime().toString();

  // 创建可修改的Headers对象,复制原响应的所有头信息
  const newHeaders = new Headers(originalResponse.headers);
  // 添加自定义缓存创建时间头
  newHeaders.set("X-Cache-Creation-Date", responseDate);

  // 基于原响应创建新的可缓存Response,保留原状态、文本和body
  const response = new Response(originalResponse.body, {
    status: originalResponse.status,
    statusText: originalResponse.statusText,
    headers: newHeaders
  });

  // 缓存新响应
  if (response.ok) {
    await cache.put(request, response.clone());
    console.info("Cached response as", response.url);
  }
  return response.clone();
}

后续缓存清理逻辑

当需要程序化删除过期缓存时,只需从缓存中取出Response后,读取自定义头的值进行判断:

async function cleanupExpiredCache() {
  const cache = await caches.open(CACHE_NAME);
  const now = Date.now();
  const maxAge = 7 * 24 * 60 * 60 * 1000; // 7天有效期

  const keys = await cache.keys();
  for (const key of keys) {
    const response = await cache.match(key);
    if (!response) continue;

    const cacheDate = response.headers.get("X-Cache-Creation-Date");
    if (cacheDate && now - parseInt(cacheDate) > maxAge) {
      await cache.delete(key);
      console.info("Deleted expired cache for", key.url);
    }
  }
}

关键说明

  1. 遵循规范避免兼容性问题:原Response的Headers不可变是Fetch API的规范要求,跨浏览器必须遵循此规则才能保证稳定运行。
  2. 自定义头的可靠性:使用X-前缀的自定义头是前端常用约定,不会与标准HTTP头冲突,且能被Cache API正常存储和读取。
  3. Body处理注意事项:创建新Response时直接使用原Response的body即可,后续的clone()调用是为了避免body被一次性消耗,确保缓存和返回操作都能正常执行。

内容的提问来源于stack exchange,提问作者kguzek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 13:45:38