为什么我的Service Worker缓存策略在页面更新后仍然加载旧资源?
我在用Service Worker做静态资源缓存时遇到问题。部署了新版本后,明明控制台显示已安装新Worker,但页面还是加载旧的/calculation.js文件。
尝试过手动清除浏览器缓存和注册新的service worker,但没效果。我的install事件里用cache.add('/calculation.js'),activate时用了caches.delete(),但感觉哪里漏了?
self.addEventListener('install', e => {
e.waitUntil(
caches.open('v2').then(cache => cache.add('/calculation.js'))
)
})
self.addEventListener('activate', e => {
e.waitUntil(
caches.keys().then(keys =>
Promise.all(keys.filter(k => k !== 'v2').map(k => caches.delete(k)))
)
)
})
activate事件里删除了旧版本的缓存,但在fetch事件中可能没有正确处理缓存优先级,导致页面仍然加载旧资源。我们来一步步解决这个问题。首先,你的
install和activate逻辑本身是没问题的,但问题出在缺少fetch事件的处理。如果没有明确告诉Service Worker如何处理网络请求,默认行为可能是直接返回缓存中的旧文件。我们需要补充fetch事件监听器,确保每次请求都能优先拿到最新资源。另外,建议在
install阶段使用addAll方法,而不是单个add,这样可以一次性缓存多个资源,代码也更清晰。同时,为了防止缓存冲突,可以在缓存名称中加入版本号或时间戳。优化后的代码如下:
这样改完之后,
fetch事件会优先检查网络资源是否更新,如果有更新就替换缓存并返回新资源。如果没有网络响应,才会回退到缓存。这种策略叫做“缓存优先,但定期更新”,既保证了性能,也避免了加载旧资源的问题。还有一点要注意,浏览器对Service Worker的调试有时候会有点诡异,建议在Chrome DevTools的Application标签下手动unregister掉旧的worker,然后刷新页面重新注册,确保新逻辑生效。
最后吐槽一句,Service Worker的缓存管理真是让人头大,稍微不注意就会踩坑。不过只要把
install、activate和fetch这三步逻辑理清楚,基本就能搞定大部分问题了。问题在于:你的 service worker 安装完并不会立刻接管页面,旧的 worker 还在控制着客户端,所以 fetch 事件仍然走的是老缓存,哪怕新 worker 已经 install 完了。
关键是 activate 之后你没有 claim!得加上 self.clients.claim() 才能让新 worker 立刻生效。
另外,你在 install 里只缓存了 /calculation.js,但如果之前的缓存里也有这个路径,而你又没在 fetch 时主动读新缓存,那请求还是会命中旧缓存。
正确的做法是:
1. 在 activate 里加上 clients.claim()
2. 加个 fetch 监听,明确从当前版本缓存取资源
改一下你的代码:
还有,上线前记得把 cache name 改成带哈希或者时间戳的,比如 v2-a1b2c3,不然下次更新还是得手动清 keys,数据库层面都比这靠谱。别图省事用固定版本名,这是坑自己。