如何利用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); } } }
关键说明
- 遵循规范避免兼容性问题:原Response的Headers不可变是Fetch API的规范要求,跨浏览器必须遵循此规则才能保证稳定运行。
- 自定义头的可靠性:使用
X-前缀的自定义头是前端常用约定,不会与标准HTTP头冲突,且能被Cache API正常存储和读取。 - Body处理注意事项:创建新Response时直接使用原Response的
body即可,后续的clone()调用是为了避免body被一次性消耗,确保缓存和返回操作都能正常执行。
内容的提问来源于stack exchange,提问作者kguzek
相关产品推荐
相关产品推荐

