当前位置:首页 > 文章列表 > 文章 > 前端 > Service Worker 更新后如何避免旧缓存继续返回

Service Worker 更新后如何避免旧缓存继续返回

来源:17golang原创 2026-09-12 12:11:11 0浏览 收藏

Service Worker 更新后仍返回旧页面,通常不是“浏览器没有更新”,而是旧 worker、旧 Cache API 条目和仍被旧 controller 控制的页面叠在了一起。稳定做法是让缓存名跟随版本变化,在 activate 中删除旧名,再决定页面何时接管新 worker。

要点速览
  • 用版本化缓存名区分资源集合,不要在新旧版本间复用含义不清的固定名称。
  • 清理旧缓存放在 activate,并通过 event.waitUntil() 等待清理完成。
  • skipWaiting() 解决等待激活,clients.claim() 解决已打开页面的控制权;两者不是同一个动作。

先把旧缓存问题拆成三个状态

一次更新至少涉及三个对象:浏览器正在安装的新 worker、仍负责当前页面请求的旧 worker,以及 Cache API 中按名称保存的资源。新 worker 安装期间旧 worker 继续工作,因此不能在 install 阶段删除旧缓存,否则旧页面可能突然拿不到自己的资源。

另一个容易忽略的边界是页面控制权。worker 激活后,已经打开的文档未必立刻换 controller;如果页面没有重新加载,看到旧内容并不能直接证明缓存清理失败。

Service Worker 更新中的安装、激活、版本化缓存和旧缓存清理静态关系示意图
图1:Service Worker 更新的静态结构示意图,重点看新 worker、当前缓存与旧缓存之间的生命周期边界;这不是运行截图。

用版本化缓存名让 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 响应格式是否兼容。更新策略是产品行为,不是越快接管越好。

Service Worker controller、clients.claim、页面刷新和 fetch 回退边界静态关系示意图
图2:controller 与缓存响应的边界示意图,展示接管、刷新和网络回退各自解决什么问题;这不是运行截图。

仍然返回旧内容时按三层证据排查

看到的现象优先检查对应判断
缓存列表里有 v2、v3activate 是否执行,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 是否应该默认开启?

只有新旧代码能兼容当前页面状态时才适合立即接管。编辑器、支付流程或长表单更适合提示刷新并等待用户确认。

相关资料:MDN PWA 缓存指南MDN Using Service Workers

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go TLS 客户端只在生产环境报证书错误怎么办Go TLS 客户端只在生产环境报证书错误怎么办
上一篇
Go TLS 客户端只在生产环境报证书错误怎么办
LiblibAI下载LoRA后怎么在本地WebUI加载?从底模匹配到权重测试
下一篇
LiblibAI下载LoRA后怎么在本地WebUI加载?从底模匹配到权重测试
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    99次使用
  • OpenCompass大模型评测体系详解:功能、使用指南与应用场景
    OpenCompass
    OpenCompass是上海AI实验室推出的开源大模型评测平台,提供CompassKit、CompassHub和CompassRank三大核心组件,支持LLM及多模态模型的一站式标准化评估与排行榜查询。
    28次使用
  • AGI-Eval大模型评测平台:权威榜单、数据集与人机协同评测方案
    AGI-Eval
    AGI-Eval是由上海交大等高校联合发布的大模型评测社区,提供公正透明的LLM能力榜单、多领域评测集及Data Studio数据服务,助力AI模型性能评估与NLP科研开发。
    6次使用
  • SuperCLUE中文大模型评测基准:功能、能力维度与应用指南
    SuperCLUE
    SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
    253次使用
  • C-Eval中文评测基准:大语言模型多学科能力评估指南
    C-Eval
    深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
    180次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码