題幹與適用場景
PWA 發布后,部分使用者仍由旧 Service Worker 控制,加载旧 JavaScript 与新 HTML 的组合,出现 ChunkLoadError。请说明生命周期、快取版本、開啟分頁、skipWaiting、clientsClaim 和回復之间的关系。
这道题适合前端、Web 平台和全栈岗位。题目要求讨论使用者体验与部署安全,不要求绑定某个框架。錯誤率和旧快取比例是练习資料,不是固定阈值。
面試官考察點
生命周期
候选人要说清 install、waiting、activate 和控制頁面的先后关系,尤其是更新 worker 默认等待旧客户端退出。
资产一致性
要区分 HTML、hashed JavaScript、运行时 API 和 precache,避免清理旧快取后仍有頁面引用旧資源。
采用策略
skipWaiting 可以加快接管,但可能让同一頁面前后使用不同版本;强回答会先评估相容性和使用者提示。
回復与观测
需要有版本标识、錯誤监控、快取保留窗口和 no-op worker 方案,确保坏版本能停止扩散。
回答前需要釐清的問題
- HTML 和静态資源是否带内容 hash?
- Service Worker 指令碼 URL 和 scope 是否稳定?
- 頁面是否可能长时间開啟或有未保存状态?
- API 是否向后相容两个前端版本?
- 是否允许提示使用者重新整理或安排下一次导航?
- 發布平台能否快速回復 worker 指令碼和资产清单?
30 秒回答框架
“新 worker 安装成功后默认进入 waiting,旧 worker 仍控制已開啟頁面。我要先让 HTML、hashed assets 和 API 具备版本相容,再决定是否在使用者确认后 skipWaiting。快取使用版本化命名或 Workbox revision,activate 只删除已知过期快取。
客户端监听 updatefound 和 controllerchange,提示使用者在安全时机重新整理,并记录 worker 版本、ChunkLoadError 和激活耗时。发现坏版本时先停止發布或部署 no-op worker,保留旧資源,修复后再推进。”
分步驟深入解答
第一步:画出生命周期
注册后 worker 进入 installing,install 成功后变成 waiting;旧 worker 仍控制现有頁面。旧客户端退出或导航后,新 worker 才会 activate 并接管。
第二步:保证資源相容
HTML 应引用带 hash 的静态資源,API 在迁移窗口内相容旧字段。不要只改快取名,却让旧頁面请求已经删除的 chunk。
第三步:设计更新检查
保持 worker URL 稳定,浏览器会按导航和注册检查指令碼字节变化。长会话可以显式调用 registration.update,但不要频繁轮询。
~~~js const registration = await navigator.serviceWorker.ready; await registration.update(); ~~~
第四步:处理等待与提示
默认等待最安全;若产品必须立即更新,先检测未保存状态和頁面相容性,再通过 postMessage 让使用者确认 skipWaiting。controllerchange 后整页重新整理应避免循环。
第五步:清理与回復
activate 中只删除 allowlist 之外的快取。保留上一版静态資源和 manifest,发生錯誤时回復入口或部署 no-op worker,不要直接清空所有快取。
第六步:观测和验收
记录 worker 版本、快取命中、激活时间、controllerchange、資源 404 和 ChunkLoadError。灰度验证新旧頁面组合、离线启动、多个分頁和回復路径。
高品質示範回答
“ChunkLoadError 很可能来自旧 worker 控制的頁面与新部署资产不相容。新 worker 安装后会 waiting,默认要等旧客户端退出或导航,所以我不会先全局 skipWaiting。
我会使用稳定的 worker URL、带 hash 的静态資源和一段 API 相容窗口。预快取按 revision 管理,activate 只清理明确过期的快取。客户端发现 waiting worker 时提示使用者在没有未保存状态的时机重新整理;若必须立即切换,先确认新旧頁面协议相容,再发送 skipWaiting,并处理 controllerchange。
發布按版本灰度,监控 worker 版本、資源 404 和 ChunkLoadError。若新版本异常,停止入口發布或部署 no-op worker,同时保留旧資源,修复后再恢复推进。验收包括新旧分頁、离线启动、重新整理和回復。”
常見錯誤
- 认为 install 成功就会立即控制所有頁面。
- 不加 hash,直接覆盖旧 JavaScript。
- 无条件 skipWaiting,导致同一頁面混用新旧版本。
- activate 直接清空所有快取。
- 改名 worker 指令碼来“强制更新”,破坏稳定注册。
- 只測試新開啟頁面,不測試长时间開啟的旧分頁。
- API 不相容却只回復前端入口。
- 没有版本监控和 no-op 回復路径。
追問與應對
追问一:为什么新 worker 会停在 waiting?
因为旧 worker 仍控制開啟的客户端。默认生命周期会等待这些客户端关闭或导航,避免同一頁面中途切换控制者。
追问二:skipWaiting 什么时候合理?
只有在資源和頁面协议向后相容、没有未保存状态且产品能接受立即重新整理时。否则应等待使用者确认或下一次导航。
追问三:如何避免旧快取无限增长?
使用版本或 revision allowlist,在 activate 删除不再使用的快取;同时保留回復所需的上一版資源窗口。
追问四:发现坏 worker 怎么止损?
停止新入口發布,必要时部署 no-op worker 或回復稳定指令碼;保留旧資源,修复后再逐步激活。
追问五:如何证明更新安全?
測試多个分頁、离线启动、旧 HTML 加新資源、重新整理、更新提示、API 相容、錯誤监控和完整回復。