当前位置:首页 > 文章列表 > 文章 > 前端 > 前端 Service Worker 的 skipWaiting 和 clientsClaim 怎么安排版本切换

前端 Service Worker 的 skipWaiting 和 clientsClaim 怎么安排版本切换

来源:17golang原创 2026-09-08 08:07:38 0浏览 收藏

Service Worker 版本切换不要把 skipWaiting()clients.claim() 当成同一个开关:前者让 waiting worker 更早进入激活,后者让已经打开的同源页面被 active worker 接管。默认策略应是让新版本先 waiting;只有旧页面引用的资源、新缓存和新 fetch 路由能够兼容时,才考虑两者一起立即接管。

最稳妥的安排是:先在 install 中准备新缓存,activate 中完成清理和 clients.claim();非破坏更新可以自动切换,可能删除旧资源或改变协议的更新则等待用户确认或下一次导航。

要点速览
  • skipWaiting() 解决的是“新 worker 还在 waiting”,不负责让页面马上使用它。
  • clients.claim() 只能由 active worker 在 activate 阶段接管 scope 内已有页面。
  • 立即切换前先检查旧页面和新缓存的兼容性,页面侧用 controllerchange 防止重复刷新。

先把两个 API 放回 Service Worker 生命周期

一次更新至少要分清四个状态:新脚本下载后进入 install,安装成功后可能停留在 waiting;旧 worker 退出后,新 worker 才进入 activate,页面是否被它控制则由 controller 关系决定。已有页面通常不会因为新 worker 激活就自动重新加载。

能力作用位置解决的问题不能替代什么
self.skipWaiting()Service Worker 内,常放在 install让 waiting worker 请求尽快进入激活不会单独接管已有页面
self.clients.claim()active 后,常放在 activate 的 waitUntil让 scope 内已有 client 使用当前 worker 控制器不会替你准备缓存或刷新页面
controllerchange页面侧感知当前页面控制器变更不会判断新旧资源是否兼容

skipWaiting() 的返回 Promise 可以不专门等待;而 clients.claim() 应放在 activateevent.waitUntil() 中,让激活阶段的异步工作有明确的生命周期。二者一起用时,旧页面可能在一次打开过程中先由旧逻辑加载,再被新 fetch 逻辑接手,这正是版本兼容检查的入口。

Service Worker install waiting activate 与 clients.claim 控制 Fetch Handler 的生命周期关系图
图1:Service Worker 的 waiting、activate、controller 与 fetch 边界;skipWaiting 和 clients.claim 作用在不同生命周期位置。

先用资源兼容性决定是否立即接管

判断标准不是“新版本已经部署”,而是旧页面继续运行时,能不能安全调用新 worker 的 fetch 逻辑。重点看三件事:旧页面仍会请求哪些 URL,新版本是否删除或改名了这些资源,新 worker 的缓存命名和响应策略是否还能满足旧页面。

例如旧页面仍引用 /assets/app.6.js,而 v7 激活时只保留 app.7.js,并且新的 fetch 路由只从 v7 缓存取文件,那么页面在不刷新的情况下被接管,就可能出现脚本或懒加载模块找不到。此时更适合让 worker 保持 waiting,弹出“发现新版本”,由用户确认后刷新。

反过来,如果资源 URL 使用内容哈希且新旧页面都能访问,或者这次只增加了不影响旧页面的缓存条目,立即接管的风险较低。Workbox 文档也特别提醒:如果懒加载资源使用唯一版本 URL,更新时删除旧预缓存可能让正在运行的旧页面加载失败,不能只看“缓存更新成功”就打开抢占。

旧页面旧缓存与新 Service Worker 新缓存通过兼容契约连接的静态关系图
图2:版本切换前要对齐旧页面、资源缓存和新 fetch 路由;兼容时才适合立即接管。

把缓存准备、清理和接管写在正确的事件里

下面是一种适合“资源兼容、希望尽快切换”的最小写法。install 只负责准备 v7 缓存;activate 先删除不再允许的缓存,再接管页面。缓存清理失败时让 activate 失败,比激活后悄悄缺资源更容易发现问题。

const CACHE_NAME = "app-shell-v7";
const APP_SHELL = ["/", "/assets/app.7.js", "/assets/app.7.css"];

self.addEventListener("install", (event) => {
  // 先把新版本需要的资源完整放入新缓存。
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => cache.addAll(APP_SHELL)),
  );
  // 只有确认新旧资源兼容时,才跳过 waiting。
  self.skipWaiting();
});

self.addEventListener("activate", (event) => {
  event.waitUntil((async () => {
    const keys = await caches.keys();
    // 只删除明确不再允许的旧版本,避免误删运行时缓存。
    await Promise.all(
      keys.filter((key) => key.startsWith("app-shell-") && key !== CACHE_NAME)
        .map((key) => caches.delete(key)),
    );
    // active 之后再接管当前 scope 内已有页面。
    await self.clients.claim();
  })());
});

这里的 skipWaiting 不是缓存准备的替代品:如果 cache.addAll() 失败,install 本身不会成功完成。另一个边界是缓存命名,清理范围应与项目自己的前缀一致,不要把第三方库或运行时响应一并删除。

页面侧只刷新一次,并给等待策略留出入口

如果采用立即接管,页面需要监听控制器变化。否则每个 controllerchange 都调用 location.reload(),可能形成重复刷新,尤其是在注册逻辑和更新逻辑同时运行时。

let hasReloadedForController = false;

navigator.serviceWorker.addEventListener("controllerchange", () => {
  // 一个页面只为本次控制器切换刷新一次,避免循环刷新。
  if (hasReloadedForController) return;
  hasReloadedForController = true;
  window.location.reload();
});

async function askForUpdate() {
  const registration = await navigator.serviceWorker.getRegistration();
  // waiting 存在时才提示用户,不把普通注册误报成更新。
  if (!registration?.waiting) return;
  const accepted = window.confirm("发现新版本,立即刷新吗?");
  if (accepted) {
    // 只有 worker 自己支持消息协议时才发送这个命令。
    registration.waiting.postMessage({ type: "SKIP_WAITING" });
  }
}

如果不想让所有更新都自动接管,可以删掉 install 中的 skipWaiting(),保留 waiting worker;页面发现 registration.waiting 后由用户确认,再通过 postMessage 让 worker 调用 self.skipWaiting()。这比把抢占策略硬编码成永远立即更新更适合有长时间打开页面的后台系统。

使用 Workbox 时,workbox-coreclientsClaim() 会把调用安排到 activate;而旧的 workbox-core.skipWaiting() wrapper 已不建议继续使用,直接调用 self.skipWaiting() 更清楚。无论是否使用 Workbox,先确定页面资源兼容契约,再选自动还是用户确认。

发布前用一张清单决定切换方式

  • 缓存:新版本的 precache 是否在 install 成功后才允许激活,旧缓存删除名单是否明确。
  • 页面:旧 HTML 是否可能继续引用不存在的旧资源,懒加载 URL 是否仍可访问。
  • 协议:新 fetch handler 是否改变响应格式、鉴权头或离线回退语义。
  • 接管:是否确实需要当前页面立即使用新逻辑,是否有一次性刷新保护。
  • 回退:发现不兼容时能否只保持 waiting,或者恢复到可访问的旧资源。

满足兼容性条件时,skipWaiting()clients.claim() 可以缩短用户等待;不满足时,保守地等待下一次导航反而更稳。版本切换的核心不是让 worker 越快变成 active,而是让页面、缓存和 fetch 路由在同一份约束下变化。

常见问题

只调用 skipWaiting,当前页面会立刻被新 worker 控制吗?

不一定。它主要推进 waiting 到 active;已有页面是否被控制还取决于 clients.claim,或者等页面下一次导航/刷新。

clients.claim 能不能放在 install 里?

不应这样安排。clients.claim 需要 active worker 才能稳定接管,通常放在 activate 的 event.waitUntil 中。

每次发布都同时使用两个 API 可以吗?

只有在新旧页面和资源兼容时才适合。若新版本删除旧懒加载文件、改变响应协议或清理旧缓存过早,应让新 worker 保持 waiting。

页面监听 controllerchange 后为什么会刷新两次?

通常是没有设置一次性标志,或注册逻辑重复绑定了监听器。把刷新保护放在模块级状态,并确保注册代码只执行一次。

参考:MDN Service Worker APIMDN skipWaiting()MDN clients.claim()Chrome for Developers Workbox Core

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go 多个生产者场景怎么安全决定谁负责关闭 channelGo 多个生产者场景怎么安全决定谁负责关闭 channel
上一篇
Go 多个生产者场景怎么安全决定谁负责关闭 channel
Go io/fs 路径和操作系统绝对路径怎么转换
下一篇
Go io/fs 路径和操作系统绝对路径怎么转换
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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测试功能,助您快速选择最适合项目的高性能大语言模型。
    19次使用
  • SuperCLUE中文大模型评测基准:功能、能力维度与应用指南
    SuperCLUE
    SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
    177次使用
  • C-Eval中文评测基准:大语言模型多学科能力评估指南
    C-Eval
    深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
    111次使用
  • AI Prompt Library:免费AI提示词库,助力ChatGPT高效创作与营销
    AI Prompt Library
    探索AI Prompt Library免费资源库,涵盖营销、写作及多场景AI提示词。兼容ChatGPT、Claude等工具,一键复制优化输出,提升工作效率。
    38次使用
  • Generrated:DALL·E 2/3 AI绘画提示词灵感库与图像对比平台
    Generrated
    Generrated汇集9300+张DALL·E生成图像及对应提示词,支持查看完整图集、对比DALL·E 2与3版本差异,是AI绘图新手学习Prompt设计与获取创作灵感的实用工具。
    18次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码