Skip to content

SSE 与 WebSocket Markdown 流式渲染

SSE 和 WebSocket 都会持续推送文本。当这些文本是 Markdown 时,中间态经常不是合法完整文档:代码 fence 未闭合、表格还没写完、数学公式缺少结束符、Mermaid 图只到一半。Markstream 通过流式中间态处理、批量渲染和节点复用,减少闪烁和 DOM 抖动。

该怎么接

传输方式推荐
SSEEventSource 累积内容,遇到 [DONE] 后设置 final
WebSocketonmessage 里批量合并 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,或你只在服务端拼好完整文档后一次性展示。