navigator.onLine仅反映系统网络接口状态,无法检测路由器断网等真实离线场景,需配合心跳探测;应全局监听online/offline事件并防抖,封装network-status-change自定义事件通知状态变更,写操作需缓存重放,服务端仍须做安全校验。

navigator.onLine 的真实行为和常见误判
navigator.onLine 只检测浏览器是否“认为自己在线”,不发起任何网络请求。它依赖操作系统网络接口状态,比如 Wi-Fi 断开、网线拔掉会立刻变 false;但若路由器断网、DNS 故障、代理挂掉,它仍返回 true。这就是为什么用户看到“已连接”却刷不出数据——navigator.onLine 没撒谎,只是能力有限。
实际项目中必须搭配心跳探测(如定时 fetch('/health'))才能补全判断。仅靠 navigator.onLine 做逻辑熔断,大概率漏掉真实离线场景。
监听 online / offline 事件的正确注册时机与作用域
事件必须在 window 上监听,且推荐在模块初始化时立即绑定,避免因异步加载错过首次状态变更。不要在组件挂载后才加监听——页面刚打开就断网,offline 事件可能已经触发过了。
- 用
addEventListener,不用ononline/onoffline属性,便于统一管理与销毁 - 务必在页面卸载前调用
removeEventListener,否则可能引发内存泄漏(尤其 SPA 中频繁切换路由) - 注意:Safari 在某些 iOS 版本中,从后台切回前台时不会触发
online,需额外检查当前状态并手动派发
自定义全局事件 dispatchOnlineStatus 的设计要点
直接在 online/offline 回调里更新 UI 或调用业务函数,会导致耦合加重、难以测试。更合理的是封装一层语义化事件,比如 dispatchEvent(new CustomEvent('network-status-change', { detail: { isOnline: true } }))。
- 事件名别用
online,避免和原生事件冲突;network-status-change更安全 -
detail中透传isOnline布尔值,而非字符串,方便下游 switch 判断 - 如果项目用 Redux/Vuex/Pinia,这里只负责通知,状态更新应交由 store 处理,不要在事件回调里直接修改 state
- 对频繁触发的场景(如笔记本合盖再打开),加简单防抖(500ms 内重复状态变更只发一次)
熔断逻辑中哪些操作该停、哪些该缓存、哪些必须强校验
不是所有网络请求都适合被 navigator.onLine === false 直接拦截。关键看操作是否具备幂等性、是否含副作用、是否有本地 fallback。
- GET 类数据拉取(如列表、详情):可直接跳过,显示“暂无网络”,后续恢复时自动重试
- POST/PUT/DELETE 等写操作:不能丢弃,应进队列暂存(localStorage + 时间戳 + 接口签名),待重连后按序重放;注意去重(相同 body + path + method 不重复提交)
- 登录态刷新(如
/refresh-token):即使离线也得尝试,因为 token 过期后无法继续操作,此时应降级为本地过期提示+强制登出 - 第三方 SDK(如 Sentry、埋点):建议加开关控制,离线时暂停上报,避免阻塞主流程或堆积未发送日志
最常被忽略的是:前端熔断后,后端接口依然可能收到请求(比如用户禁用了 JS,或绕过前端直发 API)。所以服务端仍需做鉴权、限流、幂等校验——前端的友好提示和逻辑隔离,只是用户体验层的补丁,不是安全边界。

















