最简实时刷新应避免直接setInterval轮询,改用setTimeout串行请求+visibilityState监听页面可见性,确保请求不堆积、资源不浪费。

用 fetch + setInterval 做最简实时刷新,但别直接轮询
浏览器里做实时数据面板,第一反应往往是定时拉接口——这没错,但直接写 setInterval(() => fetch('/api/data'), 2000) 很容易翻车。接口响应慢时请求会堆积;用户切到其他标签页后,setInterval 仍运行,浪费资源还可能触发服务端限流。
更稳妥的做法是:用 setTimeout 实现“上一次请求完成后再等 X 秒发起下一次”,确保串行不堆积;同时监听 document.visibilityState,页面不可见时暂停,切回来再恢复。
- 示例逻辑:
function poll() { fetch('/api/panel').then(render).finally(() => { if (document.visibilityState === 'visible') setTimeout(poll, 3000); }); } - 首次加载后立即执行一次,避免用户打开页面先看到空白
- 3 秒间隔不是固定值,可根据数据更新频率和服务器压力调整,5–10 秒对多数监控类面板更友好
WebSocket 替代轮询的三个硬性前提
WebSocket 确实更高效,但不是所有场景都适合。它要求后端必须支持长连接、有消息分发机制(比如用 Socket.IO 或原生 ws 库),且前端需处理重连、断线静默、消息乱序等问题。
如果你的后端只是个静态 API 服务(比如 Flask/FastAPI 没配 WebSocket 路由),强行加前端 new WebSocket 会直接报 Failed to construct 'WebSocket': The URL's scheme must be either 'ws' or 'wss' 错误。
- 确认后端已暴露
ws://或wss://地址,且能处理 ping/pong 心跳 - 前端初始化时加错误捕获:
ws.onerror = () => console.warn('WS connection failed'); - 不要在
onmessage里直接操作 DOM 大量节点,先用requestIdleCallback或节流防卡顿
IntersectionObserver 控制面板可视区刷新
当面板内容较多(比如多个子图表并排),用户滚动时只让当前可见区域的数据模块刷新,能显著降低 CPU 和网络开销。这时候 IntersectionObserver 比监听 scroll 事件更轻量、更可靠。
关键点在于:观察器回调里不直接发请求,而是标记“需要刷新”,等上一个请求完成、且该模块仍在可视区内,才真正调用 fetch。
- 注册观察器时设
threshold: 0.1,表示模块 10% 进入视口就触发回调 - 每个面板容器加唯一
data-panel-id,回调中通过entries[0].target.dataset.panelId区分来源 - 避免重复请求:用
Map缓存每个 panel 的最后请求时间戳,10 秒内不再触发
渲染卡顿?先关掉 console.log,再检查 innerHTML 写法
实时数据一刷屏就卡,90% 是因为每次更新都用 element.innerHTML = dataHtml 全量重绘。尤其当面板含 SVG 图表或复杂表格时,浏览器要反复解析 HTML 字符串、重建 DOM 树、触发重排重绘。
更优解是:只更新变化的部分。比如用 document.getElementById('cpu-value').textContent = data.cpu 替代整块重写;图表类用 ECharts/Chart.js 的 setOption({ series: [...] }, true) 启用增量更新。
- 禁止在循环里拼接大段 HTML 字符串再赋给
innerHTML,极易引发内存泄漏 - Chrome DevTools 中打开 Rendering 面板 → 勾选 “Paint flashing”,可直观看到哪些区域在高频重绘
- 若必须用模板,优先选
template+cloneNode(true),比字符串插值更安全
实际部署时最容易被忽略的是服务端响应头。如果后端没加 Cache-Control: no-cache,某些代理或浏览器可能缓存了第一次返回的 JSON,导致面板看似在动,数据却始终不变。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











