Service Worker 更新后如何避免旧缓存继续返回
来源:17golang原创
时间:2026-09-12 12:11:11 442浏览 收藏
Service Worker 更新后仍返回旧页面,通常不是“浏览器没有更新”,而是旧 worker、旧 Cache API 条目和仍被旧 controller 控制的页面叠在了一起。稳定做法是让缓存名跟随版本变化,在 activate 中删除旧名,再决定页面何时接管新 worker。
- 用版本化缓存名区分资源集合,不要在新旧版本间复用含义不清的固定名称。
- 清理旧缓存放在
activate,并通过event.waitUntil()等待清理完成。 skipWaiting()解决等待激活,clients.claim()解决已打开页面的控制权;两者不是同一个动作。
先把旧缓存问题拆成三个状态
一次更新至少涉及三个对象:浏览器正在安装的新 worker、仍负责当前页面请求的旧 worker,以及 Cache API 中按名称保存的资源。新 worker 安装期间旧 worker 继续工作,因此不能在 install 阶段删除旧缓存,否则旧页面可能突然拿不到自己的资源。
另一个容易忽略的边界是页面控制权。worker 激活后,已经打开的文档未必立刻换 controller;如果页面没有重新加载,看到旧内容并不能直接证明缓存清理失败。

用版本化缓存名让 activate 负责清理
下面的最小实现把预缓存和清理分开。install 只创建新集合;activate 列出所有缓存名,删除不在保留名单中的名称。把每个异步动作交给 waitUntil,浏览器才会把安装或激活视为仍在进行。
const CACHE_NAME = "app-static-v3";
const ASSETS = ["/", "/app.js", "/styles.css"];
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
// 新版本只写入自己的缓存,避免干扰仍在服务旧页面的 worker。
return cache.addAll(ASSETS);
}),
);
});
self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(
keys
.filter((key) => key.startsWith("app-static-") && key !== CACHE_NAME)
.map((key) => {
// 删除只属于本应用且不是当前版本的缓存,不碰其他用途的名称。
return caches.delete(key);
}),
),
),
);
});
这里的判断故意保留前缀范围。直接删除所有非当前缓存,可能误删同一 origin 下由其他功能维护的 CacheStorage 名称。若应用把图片、接口响应和静态文件分成多类缓存,应把各类当前名称都放进保留集合,而不是只留下一个。
skipWaiting 和 clients.claim 要不要一起用
self.skipWaiting() 让等待中的新 worker 尽快进入 active,clients.claim() 则让激活后的 worker 尝试接管作用域内的现有页面。前者改变 worker 生命周期,后者改变页面 controller。两者都打开时,旧页面可能在一次会话中切换到新代码;如果页面状态不能跨版本兼容,更稳妥的做法是只提示用户刷新,或在用户完成编辑后再发送消息让新 worker 执行。
self.addEventListener("install", (event) => {
event.waitUntil(
// 只有确认新旧脚本可以共同处理当前页面时,才立即跳过等待。
self.skipWaiting(),
);
});
self.addEventListener("activate", (event) => {
event.waitUntil(
(async () => {
// 清理完成后再接管页面,减少新代码读到未准备好资源的窗口。
await removeOldCaches();
await clients.claim();
})(),
);
});
如果选择“下次导航生效”,可以不调用这两个方法,让浏览器按默认生命周期完成切换;如果选择“立即生效”,应同时考虑未保存表单、内存状态和 API 响应格式是否兼容。更新策略是产品行为,不是越快接管越好。

仍然返回旧内容时按三层证据排查
| 看到的现象 | 优先检查 | 对应判断 |
|---|---|---|
| 缓存列表里有 v2、v3 | activate 是否执行,waitUntil 是否等待删除 | 清理逻辑未完成或未被当前 worker 接管 |
| 缓存只有 v3,页面仍旧 | navigator.serviceWorker.controller 与页面是否重载 | 页面控制权或内存中的旧文档未切换 |
| controller 已更新但接口旧 | fetch 事件是否 cache-first、HTTP Cache-Control 是否过期 | 返回来源可能是 fetch 策略或 HTTP 缓存,不是旧 Cache API 名称 |
对 HTML 入口和带版本指纹的 JS/CSS,常见组合是“网络优先的导航请求 + 版本化静态缓存”。对于用户数据接口,不要照搬静态资源的 cache-first;更新时先确认响应状态和内容类型,再决定是否写入缓存。这样即使某一层仍有旧数据,也能快速定位到底是 worker 生命周期、页面 controller,还是请求策略造成的。
常见问题
删除旧缓存后为什么当前页面没有马上变新?
缓存清理只处理 Cache API 条目,不会自动重建已经打开的文档。检查 controller 是否变化,并在合适时机重新加载页面。
缓存名每次发布都加版本就一定安全吗?
不一定。还要在 activate 删除旧名、控制缓存数量,并确认 install 的资源清单完整,否则可能只是把旧问题换了一个名称。
skipWaiting 是否应该默认开启?
只有新旧代码能兼容当前页面状态时才适合立即接管。编辑器、支付流程或长表单更适合提示刷新并等待用户确认。
-
文章 · 前端 | 2个月前 | 前端 · 缓存 · Service Worker · 白屏 · 发布故障 · 缓存策略 前端白屏 Service Worker CacheStorage 资源404 发布回滚469 收藏
-
文章 · 前端 | 2星期前 | 前端 · javascript · pwa · Service Worker · 离线缓存 · Service Worker CacheStorage Cache API caches.open caches.match caches.delete418 收藏
-
文章 · 前端 | 4天前 | pwa · Service Worker · 前端缓存 · 缓存更新 Service Worker skipWaiting clients.claim Cache Storage251 收藏
-
文章 · 前端 | 4天前 | 前端 · pwa · Service Worker · 缓存更新 · Service Worker skipWaiting clientsClaim 缓存版本 前端缓存更新410 收藏
-
127 收藏
-
304 收藏
-
483 收藏
-
文章 · 前端 | 20小时前 | 前端 · web components · 生命周期 · 自定义元素 · document Web Components Custom Elements adoptedCallback adoptNode importNode382 收藏
-
215 收藏
-
154 收藏
-
147 收藏
-
107 收藏
-
221 收藏
-
199 收藏
-
106 收藏
-
483 收藏
-
文章 · 前端 | 1天前 | 前端开发 · 网络请求 · Fetch API · 异步取消 · ReadableStream · Fetch AbortController ReadableStream Response.Body AbortError492 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习