网络状态监听与事件驱动:online/offline事件、navigator.onLine、重连检测

网络状态感知是离线同步的起点。UI 需要在网络中断时立即切换为离线模式,在网络恢复时触发数据回传与 UI 更新。本章讲解浏览器原生网络事件与 navigator.onLine 的准确含义、典型陷阱以及重连检测策略。

3.1 浏览器网络状态 API 概览

API / 属性 类型 特点 局限性
window.addEventListener('online', fn) 事件 浏览器认为网络可用时触发 不一定代表真实可达性
window.addEventListener('offline', fn) 事件 浏览器认为网络断开时触发 Wi-Fi 连接但无外网时可能不触发
navigator.onLine 属性(布尔) 实时读取当前网络状态 仅反映本地连接,非端到端连通性
navigator.connection (Network Information API) 对象 提供连接类型、下行速度、RTT 等 非标准、Chrome 为主,兼容性较差
核心认知:online/offline 事件与 navigator.onLine 只能检测“本地网络接口是否可用”,不能代表“服务端是否可达”。对于离线同步场景,必须配合心跳探测或业务请求超时作为补充。

3.2 事件驱动架构:注册与取消

以下代码展示在 Vue 3 Composition API 中注册网络事件监听,并利用 onUnmounted 完成清理,避免内存泄漏。

import { ref, onMounted, onUnmounted } from 'vue'

const isOnline = ref(navigator.onLine)

function handleOnline() {
  isOnline.value = true
  console.log('[Net] 网络恢复')
  // 触发离线队列同步
  syncOfflineQueue()
}

function handleOffline() {
  isOnline.value = false
  console.log('[Net] 网络断开')
  // UI 切换为离线模式
  enterOfflineMode()
}

onMounted(() => {
  window.addEventListener('online', handleOnline)
  window.addEventListener('offline', handleOffline)
})

onUnmounted(() => {
  window.removeEventListener('online', handleOnline)
  window.removeEventListener('offline', handleOffline)
})
注意:navigator.onLine 的初始值应在页面加载时立即读取,因为 online/offline 事件在连接状态变化时才会触发,首次加载可能错过。

3.3 navigator.onLine 的常见陷阱

  • 操作系统差异: macOS 下 Wi-Fi 连接但无互联网时,Chrome 可能仍返回 true;Android 下断开移动数据后通常会正确返回 false。
  • 浏览器兼容: 部分老旧浏览器(IE 9 以下)不支持 navigator.onLine,需要降级为定时探测。
  • 304 / DNS 缓存: 即便网络已断开,浏览器可能因缓存未失效而短暂认为在线。
  • 事件重复触发: 网络状态频繁切换时,online/offline 可能连续触发多次,需要防抖处理。

3.4 重连检测策略

仅靠原生事件无法满足离线同步的可靠性要求,通常需要结合以下策略实现更精准的重连检测:

  1. 心跳探测: 定期向服务端发送 HEAD 或 GET 请求(例如每 10s),若连续 n 次超时则判定离线。
  2. 业务请求回退: 使用 fetch 或 axios 的 timeout 配置,将超时视为离线,触发队列暂存。
  3. 指数退避重试: 重连失败后,等待时间依次增加(1s、2s、4s、8s…),避免高频无效请求。
  4. 优先级队列: 恢复连接后,按数据重要性依次同步,先同步元数据(如 刷新),再同步业务数据。

3.5 流程图:网络状态变化与 UI 响应

网络状态变化与 UI 响应流程 网络正常 (online) offline 事件触发 UI 切换离线模式 启动心跳探测 (10s) 探测成功? online 事件触发 同步离线队列数据 UI 恢复在线模式 失败(指数退避) 说明 1. 网络断开后,UI 立即切换为离线模式,用户可继续操作(数据缓存到本地队列)。 2. 心跳探测以指数退避策略持续进行检测。 3. 探测成功 → online 事件触发 → 批量同步离线数据 → UI 恢复在线模式。

3.6 调试与验证方法

  • 浏览器 DevTools Network 面板: 勾选 Offline 复选框模拟网络断开,观察事件触发情况。
  • Chrome DevTools 的 Network conditions: 可模拟不同网络类型(3G、慢速 4G)以及离线状态。
  • 控制台日志: 在 handleOnline / handleOffline 中加入 console.timeStamp(),精确记录事件发生时刻。
  • 自定义离线测试页: 使用 window.dispatchEvent(new Event('offline')) 手动触发事件,验证 UI 响应逻辑。

3.7 常见错误与优化

常见错误 现象 解决方案
未移除事件监听 页面卸载后仍触发回调,后台产生冗余网络请求 在 beforeUnmount / onUnmounted 中调用 removeEventListener
频繁触发同步 网络恢复瞬间多次触发 online 事件,导致数据重复同步 加入防抖(debounce)或节流(throttle),例如 2s 内只同步一次
仅依赖原生事件 用户计算机休眠唤醒后事件不触发,导致“假在线” 开启定时心跳检测(如每 10s 一次),作为事件机制的补充
同步顺序混乱 先同步大数据造成小数据延迟 设计优先级队列,先同步鉴权、元数据,再同步业务数据
性能考量: 心跳探测间隔不宜过短,建议不少于 5s,避免频繁请求消耗电量与带宽。移动端可用 navigator.connection.onchange 事件触发更精细的网络变化检测,但需确认浏览器兼容性。

3.8 小结