缓存优先策略必须在 fetch 事件中显式调用 caches.match() 并用 event.respondWith() 包裹,install 阶段仅预存资源无效;需处理匹配失败回退、activate 清理旧缓存、response.clone() 避免流耗尽。
缓存优先策略必须在 fetch 事件里手动 match,不是 install 阶段加了就生效
很多人注册完
、在
阶段调了
,就以为“缓存优先”自动跑起来了——其实完全没拦截请求。Service Worker 不会默认把缓存当第一响应源,必须在
事件中显式调用
并用
包裹整个逻辑链。
常见错误现象:
页面刷新后仍发网络请求,DevTools Network 面板看到 status 200 而非 from cache
断网时白屏或报错,而不是返回缓存的 HTML
返回
,但代码没处理 fallback,直接 return 了空值
实操建议:
每个
监听器开头必须写
,否则浏览器走默认流程
是 Promise,要用
处理结果,不能直接
匹配不到时,务必接一个
回退,否则请求直接失败
哪些请求适合缓存优先?哪些绝对不能缓存
缓存优先只对特定请求有效,盲目套用会导致数据错乱或命中率极低。
立即学习
“
前端免费学习笔记(深入)
”;
适用场景(推荐缓存优先):
静态资源:HTML 入口页(如
)、带哈希的 CSS/JS(如
)、图标、字体
同源、可克隆(
)、GET 方法的请求
不带查询参数的路径;若必须带参(如
),需提前 normalize URL 或用
禁止缓存优先的场景:
使用HTML,CSS,JavaScript开发Android应用程序 英文文字pdf版附源文件
如果你了解HTML,CSS和JavaScript,您已经拥有所需的工具开发Android应用程序。本动手本书展示了如何使用这些开源web标准设计和建造,可适应任何Android设备的应用程序 - 无需使用Java。您将学习如何创建一个在您选择的平台的Android友好的网络应用程序,然后转换与自由PhoneGap框架到一个原生的Android应用程序。了解为什么设备无关的移动应用是未来的潮流,并开始构建应用程序,提供更
下载
带敏感参数的接口:
、
——
按完整 URL 匹配,不同 token 就是不同 key,缓存爆炸且不安全
POST / PUT / DELETE 请求:Cache API 不支持缓存非 GET 请求的响应
动态 HTML(如服务端渲染的个人中心页):内容实时变化,缓存优先会返回过期信息
activate 阶段不清理旧缓存,设备存储会悄悄爆掉
每次更新
,如果只改了
(比如从
改成
),旧缓存不会自动消失。用户用几个月后,
可能返回十几个历史版本,占用几十 MB 存储,且
可能意外命中错误版本。
实操建议:
在
事件中调用
不要在
里做
+ 逐条
检查——太慢,可能被浏览器中断
搭配
,确保新 SW 立即接管当前页面,避免新旧缓存混用
调试时可在 DevTools > Application > Cache Storage 手动删缓存,验证清理逻辑是否生效
response.clone() 不是可选项,是必选项
对象的 body 是一个 ReadableStream,只能被读取一次。如果你在缓存写入前没 clone,
会消耗流,导致后续
报错
或直接返回空响应。
正确写法永远是:
容易踩的坑:
只在
里 clone,但忘了
时用原始对象
对非 200 响应(如 404、500)也尝试 clone 并缓存——这类响应通常不该进缓存,且部分浏览器不支持缓存 error response
在
阶段用
时,传入的 URL 列表里包含重定向响应(302)——
会失败,需先 fetch 再 put
缓存优先不是“设个开关就完事”,它依赖 install 预置、fetch 显式匹配、activate 清理、response 正确复用这四个环节严丝合缝。少一个,要么不生效,要么存一堆垃圾,要么某天突然返回错数据。
sw.jsinstallcaches.addAll()fetchcaches.match(event.request)event.respondWith()caches.match()undefinedfetchevent.respondWith(...)caches.match().then()if (response) return responsefetch(event.request)'/index.html''/js/app-8a3b2c.js'response.type === 'basic'/api/config?env=prodignoreSearch: true'/api/user?token=abc123''/auth/login'caches.match()sw.jscache_name'v1''v2'caches.keys()caches.match()activatecaches.keys().then(keys => Promise.all(keys.filter(k => k !== CURRENT_CACHE_NAME).map(k => caches.delete(k))))activatecache.keys()match()self.clients.claim()Responsecache.put(request, response)return responseTypeError: Failed to execute 'clone' on 'Response': Response body is already usedfetch(event.request).then(networkResponse => {
if (networkResponse && networkResponse.status === 200) {
const toCache = networkResponse.clone(); // ← 必须在这里 clone
caches.open(CURRENT_CACHE_NAME).then(cache => cache.put(event.request, toCache));
}
return networkResponse; // ← 原始 response 还可用
})cache.put()returninstallcache.addAll()addAll