html离线更新检测必须依赖/version.json端点返回的版本号变化,且该请求需绕过所有缓存层;每次部署须变更cachestorage名称并清理旧缓存;html文件必须显式加入cache.addall()且用绝对路径,fetch事件中需为document请求提供/offline.html兜底。

HTML离线更新检测不能靠 reload 或时间戳
浏览器不会因为你改了 JS 文件内容或重发了一次部署,就自动拉新 HTML。它只认 /version.json 这个端点返回的版本号是否变化——而且这个请求必须绕过所有缓存层。
常见错误现象:window.__APP_VERSION 在 HTML 里写死为 "a1b2c3d",但用户刷新后还是旧版,控制台也看不到报错。问题不在 JS,而在你没让浏览器真正“看到”新版本。
-
/version.json必须返回纯 JSON(如{"version": "b4c5d6e"}),响应头强制禁用缓存:Cache-Control: no-cache, must-revalidate - 服务端不能走 CDN 缓存,Nginx 配置要加:
add_header Cache-Control "no-cache, must-revalidate"; - 前端 fetch 时别带
cache: 'default',显式设为cache: 'no-store',避免被 HTTP 层拦截 - 比对逻辑得在页面加载后立即执行,不能等 DOMContentLoaded 后才查——否则用户可能已看到旧结构
Service Worker 缓存失效必须换缓存名 + 清理旧缓存
复用同一个缓存名(比如一直用 caches.open('v1'))等于把新旧资源混在一个桶里,cache.addAll() 不会覆盖,只会叠加。结果就是离线时随机返回旧 JS、新 HTML,功能直接断裂。
典型表现:用户打开页面,控制台报 Cannot find module './utils.js',但构建产物里明明有——因为 SW 加载的是上一版缓存里的 HTML,却试图执行当前版 JS 的 import 路径。
- 每次部署都要变更缓存名,例如从
v1→v2,并在install事件中调用caches.open('v2') -
activate事件里必须清理旧缓存:caches.delete('v1'),否则磁盘空间会持续膨胀 - 激活新 SW 前调用
self.skipWaiting(),再在activate里调用clients.claim(),确保新 SW 立即接管,不卡在 waiting 状态 - 不要在
install里用fetch('/index.html').then(r => r.text())动态读取 HTML 内容再缓存——这会让 SW 安装失败(跨域或 no-cors 响应无法读 body)
HTML 文件本身没进缓存 = 离线白屏
很多人只缓存了 /app.js 和 /style.css,却漏掉 /index.html。结果网络一断,浏览器连主文档都拿不出来,直接空白页,控制台连 Failed to load resource 都不报——因为根本没触发 fetch 事件。
关键点不是“路径对不对”,而是“HTML 是否被 cache.addAll() 显式包含”。相对路径容易出错:sw.js 在 /js/sw.js,写 './index.html' 会被解析成 /js/./index.html,404。
- 清单里一律用绝对路径:
['/', '/index.html', '/app.js'] -
/表示根路径,必须确保服务端对GET /返回的是静态 HTML(不能是 SSR 动态页,且响应头不能含Cache-Control: no-store) - 如果首页是
/app/,那缓存列表就得写['/app/', '/app/index.html'],不能只写'/app/'—— 浏览器不会自动补index.html - 构建时生成的哈希变量(如
window.__APP_VERSION)要注入 HTML 模板,而不是靠 JS 动态写入——后者离线时根本执行不了
fetch 事件里没 fallback 就等于没离线能力
写了 caches.match(event.request) 却没处理未命中情况,是离线白屏最常见原因。SW 不会自动 fallback 到网络或离线页,它只会让 Promise reject,最终渲染空白。
注意:不是所有请求都该 fallback 到网络。HTML 和关键静态资源可以兜底到预存的 /offline.html,但 API 请求应该直接 reject 或返回空数据,否则会误导用户。
-
event.respondWith()必须包裹完整响应链,不能只写caches.match(...).then(...)而不处理undefined - 兜底逻辑要分类型:
event.request.destination === 'document'才返回/offline.html;图片、字体等可直接 reject 让浏览器显示占位符 -
caches.match()对 URL 完全敏感,/api/user?v=1和/api/user?v=2是两个 key,建议在 fetch 前 normalize 查询参数(如移除v=xxx) - 别在
fetch事件里直接fetch('/offline.html')—— 如果网络真断了,这行也会失败;应先caches.match('/offline.html'),失败再 fallback 到内置字符串模板
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











