为什么我的Service Worker缓存策略在页面更新后仍然加载旧资源?

Zz东慧 阅读 25

我在用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)))
    )
  )
})
我来解答 赞 3 收藏
二维码
手机扫码查看
2 条解答
皇甫奕诺
你遇到的问题其实是Service Worker缓存策略中一个常见的坑。虽然你在activate事件里删除了旧版本的缓存,但在fetch事件中可能没有正确处理缓存优先级,导致页面仍然加载旧资源。我们来一步步解决这个问题。

首先,你的installactivate逻辑本身是没问题的,但问题出在缺少fetch事件的处理。如果没有明确告诉Service Worker如何处理网络请求,默认行为可能是直接返回缓存中的旧文件。我们需要补充fetch事件监听器,确保每次请求都能优先拿到最新资源。

另外,建议在install阶段使用addAll方法,而不是单个add,这样可以一次性缓存多个资源,代码也更清晰。同时,为了防止缓存冲突,可以在缓存名称中加入版本号或时间戳。

优化后的代码如下:

const CACHE_NAME = 'v2';
const ASSETS_TO_CACHE = ['/calculation.js'];

self.addEventListener('install', e => {
e.waitUntil(
caches.open(CACHE_NAME).then(cache => cache.addAll(ASSETS_TO_CACHE))
);
});

self.addEventListener('activate', e => {
e.waitUntil(
caches.keys().then(keys =>
Promise.all(keys.filter(key => key !== CACHE_NAME).map(key => caches.delete(key)))
)
);
});

self.addEventListener('fetch', e => {
e.respondWith(
caches.match(e.request).then(cachedResponse => {
if (cachedResponse) {
// 检查是否有更新版本
return fetch(e.request).then(networkResponse => {
caches.open(CACHE_NAME).then(cache => cache.put(e.request, networkResponse.clone()));
return networkResponse;
}).catch(() => cachedResponse); // 如果网络失败,回退到缓存
}
// 如果没有缓存,则直接从网络获取
return fetch(e.request);
})
);
});


这样改完之后,fetch事件会优先检查网络资源是否更新,如果有更新就替换缓存并返回新资源。如果没有网络响应,才会回退到缓存。这种策略叫做“缓存优先,但定期更新”,既保证了性能,也避免了加载旧资源的问题。

还有一点要注意,浏览器对Service Worker的调试有时候会有点诡异,建议在Chrome DevTools的Application标签下手动unregister掉旧的worker,然后刷新页面重新注册,确保新逻辑生效。

最后吐槽一句,Service Worker的缓存管理真是让人头大,稍微不注意就会踩坑。不过只要把installactivatefetch这三步逻辑理清楚,基本就能搞定大部分问题了。
点赞
2026-02-19 14:09
设计师乐萱
你这问题出在缓存版本控制上,根本不是什么浏览器抽风。虽然你开了 v2 缓存,也删了旧的 key,但你没处理好 install 阶段的缓存命中逻辑。

问题在于:你的 service worker 安装完并不会立刻接管页面,旧的 worker 还在控制着客户端,所以 fetch 事件仍然走的是老缓存,哪怕新 worker 已经 install 完了。

关键是 activate 之后你没有 claim!得加上 self.clients.claim() 才能让新 worker 立刻生效。

另外,你在 install 里只缓存了 /calculation.js,但如果之前的缓存里也有这个路径,而你又没在 fetch 时主动读新缓存,那请求还是会命中旧缓存。

正确的做法是:

1. 在 activate 里加上 clients.claim()
2. 加个 fetch 监听,明确从当前版本缓存取资源

改一下你的代码:

self.addEventListener('install', e => {
e.waitUntil(
caches.open('v2').then(cache => cache.add('/calculation.js'))
)
})

self.addEventListener('activate', e => {
e.waitUntil(
Promise.all([
caches.keys().then(keys =>
Promise.all(keys.filter(k => k !== 'v2').map(k => caches.delete(k)))
),
self.clients.claim() // 关键!立刻接管所有页面
])
)
})

self.addEventListener('fetch', e => {
e.respondWith(
caches.match(e.request).then(r => r || fetch(e.request))
)
})


还有,上线前记得把 cache name 改成带哈希或者时间戳的,比如 v2-a1b2c3,不然下次更新还是得手动清 keys,数据库层面都比这靠谱。别图省事用固定版本名,这是坑自己。
点赞 2
2026-02-12 09:32