Service Worker 注册失败主因是页面未走 HTTPS(localhost 除外)或注册路径错误;HTML 离线缓存需在 install 阶段预缓存、fetch 中拦截 document 请求并禁用 navigation preload,同时注意路由、HTTP 头与服务器配置协同。

Service Worker 注册失败的常见原因
页面没走 HTTPS(本地 localhost 除外),或者注册脚本路径写错,是最常卡住的第一步。浏览器根本不会执行 navigator.serviceWorker.register(),也不会报错,只是静默忽略。
- 确保页面在
https://或http://localhost下打开,http://127.0.0.1都不行 -
register()的路径是相对于页面 URL 的,不是 JS 文件位置 —— 比如页面在/blog/post.html,想注册/sw.js,就得写navigator.serviceWorker.register('/sw.js') - 检查 DevTools → Application → Service Workers,看 “Status” 是不是 “Waiting” 或 “Active”,如果是 “Redundant”,说明注册过但被替换了,可能是因为 sw.js 内容没变,浏览器不触发更新
缓存 HTML 页面时必须绕开 navigation preload
直接用 cache.put(request, response) 缓存 HTML 很容易失效:用户首次访问时 SW 还没激活,HTML 请求就已发出,根本没进 fetch 事件;后续刷新又可能命中预加载(navigation preload),跳过你的缓存逻辑。
- 必须在
install事件里预缓存关键 HTML,比如cache.addAll(['/', '/offline.html']) - 在
fetch事件中拦截request.destination === 'document'的请求,优先 match cache,再 fallback 到 network - 禁用 navigation preload(除非你明确处理了它):
event.respondWith(caches.match(event.request) || fetch(event.request))不够,得加判断:if (event.request.destination === 'document') { ... }
HTML 缓存更新不及时的根源
HTML 文件通常带哈希或版本号,但很多人只缓存了 /index.html 字面量路径,没处理重定向、路由模式(如 Vue Router 的 history 模式)、或服务端返回的 304/200 差异。
- 不要只缓存
'/',也要缓存你实际返回 HTML 的所有路径,比如'/app/'、'/user/profile' - 如果后端对 HTML 做了 ETag 或 Last-Modified,
fetch()可能返回 304,这时 response body 为空,cache.put()会失败 —— 必须 clone 并检查response.status !== 200 - Vue/React 路由的 HTML fallback(如 Nginx 的
try_files $uri $uri/ /index.html)必须和 SW 缓存策略对齐,否则离线时 SW 返回了旧index.html,但 JS bundle 却是新的,出现白屏或 React hydration error
调试 HTML 离线加载失败的关键检查点
DevTools 里看到“Failed to load resource”,但不确定是网络、缓存还是解析问题?先盯住三个地方:
立即学习“前端免费学习笔记(深入)”;
- Application → Cache Storage → 点开你的 cache 名称,确认
/或对应 HTML 路径是否存在,且 Response Headers 包含content-type: text/html - Network 面板勾选 “Disable cache”,再刷新,看 HTML 请求是否真的走了
fetch事件(右键请求 → “View in Service Worker”) - 在
fetch事件里加console.log(event.request.url, event.request.destination),确认 HTML 请求确实进了这个 handler,而不是被其他 SW 或浏览器机制截胡
HTML 的离线缓存不是把文件扔进 cache 就完事,它和路由、部署方式、HTTP 头、甚至服务器重定向都咬得很死。一个没配对的 Cache-Control 或多一次 302,整个链路就断了。



















