实时数据更新应依场景选通信机制:websocket适用于低延迟双向交互,需完善生命周期管理;SSE适合服务端单向推送,兼容性好且自动重连;fetch轮询则用于实时性要求低或受限环境,须加节流与退避;MutationObserver不属实时数据更新方案。

javaScript 实现实时数据更新,核心不是轮询或长连接本身,而是根据场景选对通信机制——多数情况下,WebSocket 是最直接的解法;但若后端不支持、或只是轻量级页面刷新,EventSource(SSE)或带节流的 fetch 轮询更稳妥。
用 WebSocket 建立双向实时通道
适用于需要低延迟、双向交互的场景(如聊天、协同编辑、监控看板)。关键点不在连接建立,而在连接生命周期管理:
- 必须监听
onopen、onmessage、onerror、onclose四个事件,缺一不可;onclose中建议加指数退避重连逻辑,避免雪崩式重连 - 发送前务必检查
ws.readyState === WebSocket.OPEN,否则会静默失败 - 服务端推送的数据建议统一包裹为 jsON,前端用
json.parse(event.data)解析,避免原始字符串引发类型错误 - 页面卸载前应主动调用
ws.close(),否则可能残留连接并触发浏览器警告
const ws = new WebSocket('wss://api.example.com/realtime'); ws.onmessage = (event) => { const data = JSON.parse(event.data); updateUI(data); // 你的渲染逻辑 };
用 EventSource 实现服务端推送(SSE)
适合「服务器→客户端」单向、文本型实时更新(如通知、日志流、行情快照),兼容性好(chrome/firefox/safari/edge 全支持),且自动重连:
- 后端需返回
Content-Type: text/event-stream,每条消息以data:开头,结尾双换行(nn) - 前端实例化后,无需手动重连:
EventSource内置重试机制(默认 3s),可通过eventSource.readyState判断状态 - 不支持自定义请求头(如
Authorization),如需鉴权,改用查询参数传 Token(new EventSource('/stream?token=xxx')) - 注意:Safari 对 URL 长度敏感,带长 token 时易截断,建议 token 改用 cookie 传递
const es = new EventSource('/api/events?token=abc123'); es.onmessage = (event) => { const data = JSON.parse(event.data); renderNotification(data); };
用 fetch + setTimeout 实现可控轮询
当实时性要求不高(如每 5–30 秒刷新一次)、或环境受限(如老旧内网系统不开放 WebSocket 端口)时,轮询仍是务实选择。重点在“控”而非“刷”:
立即学习“Java免费学习笔记(深入)”;
- 避免固定间隔死循环,改用上一次请求完成后再启动下一次(即链式
fetch().then(() => setTimeout(...))),防止请求堆积 - 加入简单节流:若用户切到其他 tab,暂停轮询(监听
visibilitychange事件) - 服务端接口务必支持条件查询(如传
last_update_time),避免每次拉全量数据 - 网络异常或 5xx 错误时,应指数退避(如 1s → 2s → 4s),而非立即重试
let polling = true; const poll = () => { if (!polling) return; fetch('/api/status') .then(r => r.json()) .then(data => updateStatus(data)) .catch(() => setTimeout(poll, 2000)) // 失败后 2s 重试 .finally(() => setTimeout(poll, 5000)); // 成功后 5s 继续 };
为什么 MutationObserver 不算“实时数据更新”
有人误用 MutationObserver 监听 dom 变化来“感知数据更新”,这是本末倒置。它只响应渲染结果,不反映数据源变化,且无法跨组件/作用域同步:
- 若数据更新后因条件判断未触发 re-render,
MutationObserver就完全收不到信号 - 无法区分是 JS 主动修改、还是用户输入导致的 DOM 变化
- 性能开销隐性:观察范围稍大(如
document.body)就容易卡顿,且无标准取消时机 - 真正该用它的场景是“需要劫持第三方库的 DOM 输出”,而非自己维护的数据流
实时性的根在数据源头和传输链路,不在 DOM 层补漏。选错起点,后面所有优化都是徒劳。