跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

如何用 Service Worker 实现即使在无网环境下也能显示的离线提示页

Service Worker离线提示页必须预缓存静态/offline.html路径,仅响应导航请求(destination==='document'或mode==='navigate'),用503状态码构造Response返回,且该HTML须零外部依赖。 能实现,但必须提前缓存提示页本身,且不能依赖运行时 fetch 拦截失败后跳转——Service Worker 的离线响应必须来自 cache storage 或 self.skipWaiting() 之后的已激活状态。 注册时确保 Service Worker 已安装并激活 很多开发者以为注册完就能立刻拦截请求,其实注册只是触发 install 阶段,而 fetch 事件只在 activate 后生效。如果用户首次访问就断网,install 阶段失败(比如提示页资源没加载完),整个离线逻辑就崩了。
navigator.serviceWorker.register()
后要监听
waiting
和
active
状态,必要时调用
sw.waiting.postMessage('skipWaiting')
在
install
事件里用
event.waitUntil(caches.open('offline-v1').then(cache => cache.addAll(['/offline.html'])))
预缓存提示页,路径必须和后续 fetch 中匹配 不要在
install
里缓存动态路由(如
/user/123
),
/offline.html
必须是静态可预知路径 fetch 事件中区分导航请求与资源请求 浏览器对页面导航(
request.destination === 'document'
)和脚本/CSS/图片等资源的处理逻辑不同。离线提示页只应响应导航请求,否则可能把 JS 文件也替换成 HTML,导致白屏或报错。 用
if (request.destination === 'document')
过滤,避免误劫持
.js
、
.css
请求 检查
event.request.mode === 'navigate'
更稳妥,因为部分浏览器对直接地址栏输入也会发
destination: 'document'
不要用
fetch(request).catch(...)
做兜底——离线时 fetch 会直接 reject,但你得在 reject 前就决定返回缓存还是 fallback 返回离线页时注意响应头与状态码 直接
Response.redirect('/offline.html')
在离线时会失败(重定向需要网络),必须用
new Response(body, { status: 503, headers: { 'Content-Type': 'text/html' } })
构造完整响应。 从 cache 中读取
/offline.html
后,要用
response.text()
解析 body,再 new Response(body, options),不能直接 return response —— 因为 cache.put 保存的是 Response 对象,但 cache.match 返回的 response.body 已被读取过一次,需重新构造 状态码建议用
503
(Service Unavailable)而非
200
,便于调试时在 DevTools Network 面板一眼识别是离线 fallback 确保
/offline.html
自身不引用任何外部资源(如 CDN 字体、第三方 JS),否则它自己也会加载失败 最常被忽略的一点:缓存策略不是“有就行”,而是“install 时必须成功写入 + activate 后能稳定读取”。哪怕只差一个斜杠(比如缓存了
offline.html
却在 fetch 里匹配
/offline.html
),或者 HTML 里写了
但没缓存该 JS,离线页都会半残。真实环境里,多一个相对路径、少一个
event.waitUntil
包裹,就足以让整个离线流程静默失效。

相关文章