EventSource 接流日志比轮询更轻量,需后端返回 text/event-stream、no-cache 响应头及 data:...\n\n 格式;前端节流渲染、scrollIntoView 滚动到底部、white-space: pre-wrap + word-break: break-word 控制样式;IE11 回退 fetch 流式读取,注意无 BOM 和换行符规范。

用 EventSource 接流日志比轮询更轻量
直接轮询 /api/logs 会压垮后端,尤其高频率日志场景。浏览器原生 EventSource 支持服务端发送事件(SSE),连接保持、自动重连、文本流解析都内置好了,只要后端按 SSE 格式输出(data: ...\n\n),前端几行就能接住滚动日志。
常见错误是后端没设对响应头:Content-Type: text/event-stream 和 Cache-Control: no-cache 缺一不可,否则 Chrome 会缓存或拒绝连接。
- 后端需逐行输出
data: {"level":"INFO","msg":"user login"}\n\n,每条日志必须以data:开头 + 双换行结尾 -
EventSource不支持自定义请求头,如需鉴权,改用fetch+ReadableStream(但兼容性差) - 日志量大时,前端要节流渲染:只追加最后 500 条,用
element.innerHTML = logs.slice(-500).join('')替代频繁appendChild
滚动到底部要用 scrollIntoView 而非 scrollTop
scrollTop = element.scrollHeight 在某些 Safari 版本里失效,且手动算高度容易被 CSS margin/padding 干扰。用 scrollIntoView 更可靠,尤其配合 { behavior: 'smooth', block: 'nearest' } 避免跳帧。
关键点是:必须等新 <div> 插入 DOM 后再调用,否则目标元素还没渲染,<code>scrollIntoView 无效果。
立即学习“前端免费学习笔记(深入)”;
- 不要在字符串拼接完就调用,要在
el.insertAdjacentHTML('beforeend', line)之后 - 如果日志行数暴增(如批量刷屏),可加
requestAnimationFrame延迟到下一帧执行滚动 - 避免给容器设
overflow: hidden,否则scrollIntoView会被截断
日志行样式隔离靠 white-space: pre-wrap 和 word-break
原始日志含时间戳、堆栈、长 URL,直接 innerHTML 渲染会挤出容器或折行错乱。CSS 必须同时控制空白符处理和断行策略。
典型错误是只设 white-space: pre,导致超长行撑破仪表盘;或只设 word-break: break-all,让 JSON 键名被硬切开。
- 容器统一用
white-space: pre-wrap:保留换行和空格,允许折行 - 日志行内
<span class="log-line"></span>加word-break: break-word,优先在单词间断行 - 时间戳等固定字段用
<span class="timestamp"></span>+flex-shrink: 0防止被压缩
IE 不支持 EventSource?用 fetch + Response.body.getReader() 回退
IE11 完全不支持 EventSource,但部分老系统仍需兼容。此时不能简单降级为定时轮询,而要用 fetch 流式读取——前提是后端支持 HTTP chunked encoding。
注意:Safari 15.4 之前不支持 getReader() 的 read() 返回 Promise,得用 while 循环 + await;Chrome/Firefox 没这问题。
- 建立连接后,用
const reader = response.body.getReader()获取读取器 - 每次
reader.read()返回{ done, value },value是Uint8Array,需转字符串:new TextDecoder().decode(chunk) - 手动按
\n\n切分日志块,再解析data:前缀——这部分逻辑要和 SSE 解析对齐,否则格式不一致
最易忽略的是服务端日志编码:UTF-8 BOM 会导致 EventSource 解析失败,所有日志流输出前必须确保无 BOM,且每行末尾是 \n(不是 \r\n)。



















