SSE 与 WebSocket Markdown 流式渲染
SSE 和 WebSocket 都会持续推送文本。当这些文本是 Markdown 时,中间态经常不是合法完整文档:代码 fence 未闭合、表格还没写完、数学公式缺少结束符、Mermaid 图只到一半。Markstream 通过流式中间态处理、批量渲染和节点复用,减少闪烁和 DOM 抖动。
该怎么接
| 传输方式 | 推荐 |
|---|---|
| SSE | 用 EventSource 累积内容,遇到 [DONE] 后设置 final |
| WebSocket | 在 onmessage 里批量合并 chunk |
| 高频输出 | 用 requestAnimationFrame 或队列控制更新频率 |
| 超长回答 | 开启 node-virtual="auto" 或对应框架的 live node 控制 |
tsx
import MarkdownRender from 'markstream-react'
import { useEffect, useState } from 'react'
import 'markstream-react/index.css'
export function StreamView() {
const [content, setContent] = useState('')
const [isDone, setIsDone] = useState(false)
useEffect(() => {
const source = new EventSource('/api/chat/stream')
source.onmessage = (event) => {
if (event.data === '[DONE]') {
setIsDone(true)
source.close()
return
}
const data = JSON.parse(event.data) as { content?: string }
setContent(prev => prev + (data.content ?? ''))
}
return () => source.close()
}, [])
return <MarkdownRender content={content} final={isDone} fade={false} />
}WebSocket 只是在传输层不同,渲染层仍然是“累积内容,然后传给 renderer”。如果服务端一次推很多小 token,不要每个 token 都同步 setState;先缓冲到一帧或一小批,再提交给 UI。
不适合的情况:流里只有纯文本且没有 Markdown,或你只在服务端拼好完整文档后一次性展示。