ServiceWorker注册失败时离线页面不会生效,主因是页面非HTTPS(localhost除外)、脚本路径错误、注册时机过早或浏览器不支持;须在DOM就绪后调用register(),并检查Registration状态确认activated。

ServiceWorker注册失败时离线页面不会生效
ServiceWorker必须成功注册并激活,后续的 fetch 事件拦截才可能触发。常见失败原因包括:页面非 https(本地 http://localhost 除外)、注册脚本路径错误、浏览器不支持或禁用 ServiceWorker、注册时机过早(如 DOM 尚未 ready)。务必在 window.addEventListener('load', ...) 或 document.readyState === 'complete' 后调用 navigator.serviceWorker.register()。
实操建议:
- 注册后检查返回的
Registration对象,打印registration.active、registration.waiting、registration.installing状态,确认是否进入activated - 在 DevTools 的 Application → Service Workers 面板中勾选
Update on reload并手动skipWaiting,避免旧版本阻塞 - 注册脚本路径应为绝对路径(如
'/sw.js'),避免相对路径导致 404 注册失败
fetch事件中拦截导航请求并返回自定义离线页
仅靠缓存静态资源不够——用户刷新或直接访问某个路由时,若网络断开且该 URL 未被 precache,fetch 事件默认会抛出网络错误,页面白屏。必须显式拦截 request.destination === 'document' 的请求,并用 Response.redirect() 或 new Response() 返回离线页内容。
关键点:
- 不要只拦截
GET,要判断event.request.mode === 'navigate',这是浏览器加载页面的典型标识 - 离线页 HTML 必须提前缓存(例如在
install事件中用cache.addAll(['/offline.html'])) - 返回响应时需设置正确
Content-Type: text/html头,否则可能被解析为纯文本 - 示例逻辑片段:
self.addEventListener('fetch', event => { if (event.request.mode === 'navigate') { event.respondWith( fetch(event.request).catch(() => caches.match('/offline.html')) ); } });
缓存策略混用导致离线页无法加载
如果离线页本身依赖外部 CSS/JS(比如从 CDN 加载的框架),而这些资源未被缓存,即使 /offline.html 被匹配到,页面仍可能渲染异常或空白。ServiceWorker 不会自动递归缓存 HTML 中的 <link> 或 <script></script>。
解决方式:
- 离线页必须是「自包含」的:内联关键 CSS/JS,或把所有依赖资源(如
/offline.css,/offline.js)一并加入 install 阶段的缓存列表 - 避免在
/offline.html中使用importScripts或动态fetch,这些在离线时必然失败且无降级 - 用
caches.open('offline-v1').match('/offline.html')在控制台手动验证能否取到响应,确认缓存确实写入成功
跳转到离线页后用户无法返回原 URL
直接 caches.match('/offline.html') 返回会导致地址栏仍是原 URL(如 /article/123),但内容却是离线页——这容易引发用户困惑,且前进/后退历史混乱。更合理的做法是重定向到一个明确的离线路径(如 /__offline),并在该路径下展示提示。
注意:
- 重定向需用
Response.redirect('/__offline', 302),不能用window.location.href(SW 中无 window) -
/__offline这个路径也必须被 precache,且确保服务器(或托管平台如 Vercel/Netlify)不拦截该路径返回 404 - 若使用单页应用(SPA),还需确保客户端路由能识别
/__offline并渲染对应提示组件,而非尝试拉取远端数据
href 和 src,确认全部 200。

















