caches.open() 在普通页面脚本中调用会报 typeerror: caches is not defined 或返回 undefined,因为它仅在 service worker 全局作用域(self)中可用,且需 sw 已注册并激活;正确做法是在 sw.js 的 install 等事件中通过 await caches.open('name') 或 .then() 调用,传入合法缓存名(如 'v1-static'),并确保使用 response 对象或 url 字符串进行缓存操作。

cache.open() 为什么总是返回 undefined 或抛出 TypeError?
直接调用 caches.open() 前必须确保当前环境支持 Service Worker 且已注册激活——它**不是普通 HTML 页面脚本里能直接用的 API**。浏览器会报 TypeError: caches is not defined 或 undefined,因为 caches 对象只在 Service Worker 全局作用域(self)中可用,且依赖 navigator.serviceWorker.ready 就绪状态。
常见错误写法:<script>caches.open('my-cache')</script> → 必然失败。
正确路径是:先注册 SW 脚本,再在 SW 文件内部调用 caches.open()。
如何在 Service Worker 中安全调用 caches.open() 创建缓存空间
caches.open() 返回 Promise,必须 await 或 .then() 处理;传入的缓存名是字符串,建议用常量避免拼写错误。它不会覆盖已有缓存,而是复用同名缓存空间。
实操要点:
- 缓存名不能含
/、..、空格等非法字符,推荐只用字母、数字、短横线(如'v1-static-assets') - 同一缓存名多次调用
caches.open()返回的是同一个Cache实例,不是新建 - 若想清旧缓存建新名,需手动
caches.delete('old-name'),否则旧缓存残留 - Chrome/Firefox 支持良好,但 Safari 对
caches的matchAll()等方法支持有限,命名缓存本身无兼容问题
示例(写在 sw.js 中):
self.addEventListener('install', event => {
event.waitUntil(
caches.open('v1-pages').then(cache =>
cache.addAll([
'/',
'/index.html',
'/style.css'
])
)
);
});
Response 对象怎么进缓存?add()、addAll()、put() 选哪个?
只有 Response 对象(或能转为它的 URL 字符串)才能存进 Cache。关键区别:
-
cache.add(url):自动 fetch 该 URL,成功后存响应;失败则整个操作 reject -
cache.addAll(urls):批量执行add,任一失败即 reject,不保证原子性(部分可能已存) -
cache.put(request, response):最灵活,适合自定义响应(如 fallback 页面、伪造 200 响应),request可是Request实例或 URL 字符串
注意:cache.put() 的 response 必须是真实 Response 对象(比如从 fetch 得来),不能是普通 JSON 或字符串。常见错误是直接 cache.put('/api', {data: 1}) → 报错。
安全写法示例:
// 在 fetch 事件中拦截并缓存 API 响应
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
if (url.origin === self.location.origin && url.pathname.startsWith('/api/')) {
event.respondWith(
fetch(event.request).then(response => {
// 克隆响应(Response 流只能读一次)
const clonedResponse = response.clone();
caches.open('api-responses').then(cache =>
cache.put(event.request, clonedResponse)
);
return response;
})
);
}
});
缓存名冲突和清理时机容易被忽略
缓存名不是“作用域”,而是全局唯一标识。不同 SW 版本如果用了相同缓存名,会互相读写——这既是便利也是风险。比如 v1 SW 存了 'pages',v2 SW 想升级结构,却没删旧缓存,就可能读到过期资源。
几个硬要记住的点:
- Service Worker 更新时,旧 SW 仍控制页面,新 SW 需手动 skipWaiting + clients.claim 才能立即生效,否则新缓存逻辑不触发
-
caches.keys()可列出所有缓存名,适合做版本迁移(如删除'v0-*'缓存) - 缓存不会自动过期,
cache.match()查到的就是上次put()或add()存的原始响应,包括其 headers 和 status - 调试时可在 Chrome DevTools > Application > Cache Storage 下直接查看各缓存内容,比猜强得多
真正麻烦的从来不是“怎么开缓存”,而是“什么时候删旧缓存”和“Response 流被消费后无法二次使用”——这两个点卡住的人最多。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











