网络状态检测听起来是个小功能,但在实际项目里出问题的频率一点也不低。用户在地铁里刷页面、电梯里提交表单,网络时断时续,如果应用没有感知能力,用户只会看到一堆报错和转圈,完全不知道发生了什么。浏览器其实早就提供了标准的online和offline事件,配合React的hooks能力,我们可以用很小的代价实现一个可靠的网络状态监听与断网提示方案。这篇文章从事件原理讲到组件落地,把容易踩的坑一并说清楚。

一、online和offline事件的底层行为
浏览器在网络连接状态变化时会派发两个标准事件:online和offline。这两个事件挂载在window对象上,任何页面都可以直接监听。需要注意的是,这里的“网络状态”指的是浏览器与操作系统的网络连接状态,而不是你的服务器是否可达。也就是说,用户连上了WiFi但WiFi本身没有外网,online事件照样会触发,navigator.onLine也依然是true。
判断初始状态可以用navigator.onLine这个只读属性,它返回布尔值。但这个属性在不同浏览器上的实现粒度不同:桌面端Chrome主要跟随系统网卡状态,移动端Safari则会在实际请求失败后更新。所以它只能作为初始化参考,不能当作绝对真理。真正可靠的状态变化通知,还是依赖事件监听。事件的触发时机是在状态已经切换之后,监听器收到的就是最终状态,不需要自己去抖动处理。
还有一个细节值得注意:这两个事件不会冒泡,但在捕获阶段可以监听到。如果你在document上用{ capture: true }注册,同样有效,某些老版本浏览器甚至只支持document.body上的监听。为了兼容性,一些成熟库会选择同时在window、document和document.body上绑定监听器,只要有一个触发就以它为准。
二、用自定义Hook封装网络状态监听
在React里使用事件监听,最忌讳的就是直接写在组件里不管清理。如果组件频繁挂载卸载,每次都往window上堆监听器,很快就会内存泄漏。正确的做法是封装一个自定义Hook,在useEffect中绑定事件,并在cleanup函数中解绑。下面是一个基础版本:
import { useEffect, useState } from 'react';
function useNetworkStatus() {
// 用 navigator.onLine 初始化,避免首屏状态为空
const [online, setOnline] = useState(navigator.onLine);
useEffect(() => {
const handleOnline = () => setOnline(true);
const handleOffline = () => setOnline(false);
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
// 组件卸载时解绑,防止内存泄漏
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return online;
}这个写法在React 17及之前完全够用。但如果你用的是React 18并且开启了并发特性,更推荐用useSyncExternalStore来订阅外部状态。它的优势在于能正确处理并发渲染下的撕裂问题:React会在渲染时重新读取快照,保证同一渲染批次内的数据一致性。改造后的版本如下:
import { useSyncExternalStore } from 'react';
function subscribe(callback) {
window.addEventListener('online', callback);
window.addEventListener('offline', callback);
return () => {
window.removeEventListener('online', callback);
window.removeEventListener('offline', callback);
};
}
function useNetworkStatus() {
const online = useSyncExternalStore(
subscribe,
() => navigator.onLine, // 客户端快照
() => true // 服务端快照,SSR时默认在线
);
return online;
}两种方案怎么选?如果项目还在React 17,用useState加useEffect即可;如果已经是React 18且存在SSR场景,useSyncExternalStore是更规范的选择,它还省去了手动维护状态的样板代码,直接把navigator.onLine当作唯一数据源。
三、实现全局断网提示条组件
有了Hook之后,断网提示组件就水到渠成了。常见的设计是在页面顶部渲染一条固定的提示栏,断网时显示,恢复后自动消失或短暂显示“已恢复连接”。由于网络状态是全局性的,把组件放在应用根节点,通过Context下发状态即可,不必每个页面单独引入。下面是一个完整的提示条实现:
import { useNetworkStatus } from './useNetworkStatus';
function OfflineBanner() {
const online = useNetworkStatus();
if (online) return null;
const style = {
position: 'fixed',
top: 0,
left: 0,
right: 0,
zIndex: 9999,
padding: '10px 16px',
background: '#f56c6c',
color: '#fff',
textAlign: 'center',
fontWeight: 500
};
return (
<div style={style}>
网络连接已断开,请检查网络后重试
</div>
);
}
export default OfflineBanner;在应用入口处挂上这个组件,比如在路由外层包一层:<OfflineBanner />。这样无论用户在哪个页面断网,提示都会立刻出现。进一步优化时,可以加上恢复联网后的反馈逻辑:用一个状态记录“刚恢复”的时间窗口,在两秒内显示绿色提示条“网络已恢复”,随后淡出。这种小细节能显著降低用户的焦虑感。
除了视觉提示,更关键的是行为层面的处理。断网期间应当拦截无意义的请求:可以在请求封装层判断navigator.onLine,为false时直接reject并给出统一错误码,避免堆积大量超时请求。恢复联网后,可以触发一次数据刷新,把断网期间可能过期的列表重新拉取。如果应用中有表单提交场景,还可以把未提交的数据暂存到本地,恢复后提示用户继续操作。
四、事件方案的局限性与降级策略
前面反复提到,online和offline事件只反映系统层面的连接状态,存在两个明显的盲区。第一是“假在线”:WiFi已连接但实际无法访问互联网,此时事件不会触发,用户看到的是在线状态却请求全部失败。第二是事件不触发的情况:少数老旧浏览器或者某些WebView环境对这两个事件支持不完整,状态可能永远不更新。
针对这些问题,业界常用的补充手段是心跳探测。定期向一个轻量接口发请求,比如一张小图片或者一个HEAD请求,成功则认为真正可达,失败则判定为离线。注意探测接口要选择跨域友好、响应极快的地址,并且控制频率,一般15到30秒一次就足够,过于频繁反而增加服务端压力。心跳结果和事件监听可以合并:事件触发时立即做一次探测校验,探测结果作为最终状态,两者互相印证。
async function checkRealConnectivity() {
try {
// 加时间戳避免命中缓存
await fetch('/ping.img?t=' + Date.now(), { method: 'HEAD' });
return true;
} catch {
return false;
}
}另外还有现成的库可以考虑,比如@reamodev/react-network-status或者自己基于上述思路封装。选择库时重点看它是否同时处理了事件监听、SSR兼容和多标签页同步。对于绝大多数业务应用来说,事件监听加基础的心跳兜底已经能覆盖九成以上的场景,不必过度设计。
五、总结
网络状态检测的核心就三步:用navigator.onLine做初始判断,用online和offline事件监听变化,用提示组件和请求拦截把状态转化为用户可感知的体验。技术上不复杂,难点在于细节处理:监听器的清理、SSR环境下的兼容、假在线场景的兜底。把这些点都照顾到,你的应用在网络异常时的表现会明显好于大多数同类产品,用户遇到断网时不再是一脸茫然,而是清楚地知道发生了什么、该怎么做。
React网络状态检测Online Offline事件断网提示组件修改时间:2026-09-08 13:47:12