白屏主因是旧缓存未清理干净,须在 activate 阶段用 caches.delete() 清除非当前版本缓存,并加 event.waituntil();fetch 中需严格匹配当前缓存名、避免空响应 fallback;新 sw 安装时调用 self.skipwaiting() 并提示用户刷新;服务端 /sw.js 响应头设 cache-control: no-cache;多浏览器需针对性清残留缓存。
白屏往往不是因为没缓存,而是旧缓存还活着、新 service worker 拦不住它——关键在 activate 阶段没清干净。
必须在 activate 事件里主动清理旧缓存
install 阶段装新缓存,activate 阶段才真正“交班”。若跳过清理,旧缓存(比如 cache-v1)仍会被 caches.match() 命中,返回过期 HTML 或空响应,直接白屏。
- 在
self.addEventListener('activate', ...)中调用caches.keys(),遍历所有缓存名 - 用正则匹配旧命名规则(如
/^cache-\d+$/或cache-v1),对非当前版本缓存调用caches.delete() - 务必加
event.waitUntil(),确保清理完成再进入激活状态
fetch 里别让旧缓存“钻空子”
即使 activate 清了旧缓存,如果 fetch 逻辑写得松散,仍可能因 URL 匹配不严而误用残留资源。
- HTML 页面请求(
destination === 'document')必须走缓存优先,且只匹配当前缓存名,避免 fallback 到已删但未清完的缓存 - 静态资源(JS/CSS)建议用
{ ignoreSearch: true }匹配,防止main.js?v=123和缓存中的main.js不一致导致 miss - 不要在 fetch 里写
if (cached) return cached; else return fetch(...)这种裸 fallback——万一缓存里存的是空 Response,就会白屏
上线前强制触发一次“冷启动”更新流
用户浏览器可能长期没关,旧 SW 一直 waiting,新版本迟迟不生效。需人工干预打破僵局。
- 在新 SW 的 install 事件开头加
self.skipWaiting(),让新 SW 立即尝试接管 - 在页面 JS 中监听
controllerchange,检测到新 SW 激活后,提示用户刷新(或自动location.reload(true)) - 服务端对
/sw.js响应头加Cache-Control: no-cache,确保每次都能拿到最新脚本
火狐/UC/Edge 等浏览器要额外防残留
不同浏览器对缓存清理的激进程度不同,尤其火狐会把 Cache Storage 和 AppCache 条目分开存,容易漏清。
- 火狐:访问 about:debugging → 注销 SW + about:cache → 手动删 Application Cache 条目
- UC 安卓:开 uc://inspect → Service Workers 标签页 → Unregister,再进系统设置清 UC 应用数据
- 所有浏览器上线后,建议在 DevTools 的 Application → Cache Storage 中确认只剩一个缓存名,且内容与当前版本一致










