流式渲染 Incremark、ant-design-x markdown、streammarkdown-vue 全流程方案对比
流式 Markdown 渲染方案对比
本文档对流式 Markdown 渲染方案进行技术对比:Incremark、ant-design-x 和 markstream-vue。每个方案都有其独特的设计理念和优势。
全流程对比
ant-design-x 全流程
用户输入(流式 Markdown)
↓
┌─────────────────────────────────────────────────────────┐
│ useTyping Hook │
│ - 逐字符消费纯文本 │
│ - 输出带 fade-in 标记的文本块 │
├─────────────────────────────────────────────────────────┤
│ useStreaming Hook │
│ - 正则检测不完整 token(链接、图片等) │
│ - 缓存不完整部分,只输出完整的 Markdown │
│ ↓ │
│ Parser (marked.js) │
│ - 全量解析:content → HTML 字符串 │
│ ↓ │
│ Renderer (html-react-parser) │
│ - HTML 字符串 → React 组件 │
└─────────────────────────────────────────────────────────┘
↓
React DOM
关键特点:
- 每次内容变化时使用
marked.parse()全量解析 - 打字机动画在纯文本字符串层操作
- 使用 HTML 字符串作为中间格式
markstream-vue 全流程
用户输入(Markdown 字符串)
↓
┌─────────────────────────────────────────────────────────┐
│ 预处理 │
│ - 正则修复流式边界问题 │
│ - "- *" → "- \*",裁剪悬空标记等 │
│ ↓ │
│ markdown-it.parse() │
│ - 全量解析 → Token 数组 │
│ ↓ │
│ processTokens() │
│ - Token → ParsedNode[](自定义 AST) │
├─────────────────────────────────────────────────────────┤
│ Vue 组件渲染 │
│ - <transition> 实现渐入动画 │
│ - 节点类型 → 组件映射 │
└─────────────────────────────────────────────────────────┘
↓
Vue DOM
关键特点:
- 每次内容变化时使用
markdown-it.parse()全量解析 - 预处理层处理流式边界情况
- 使用 Vue
<transition>实现打字机效果
Incremark 全流程
用户输入(流式 Markdown chunk)
↓
┌─────────────────────────────────────────────────────────┐
│ IncremarkParser.append(chunk) │
│ - 增量更新缓冲区(只处理新增部分) │
│ - 检测稳定边界(空行、标题等) │
│ - 稳定部分 → completedBlocks(只解析一次) │
│ - 不稳定部分 → pendingBlocks(重新解析) │
│ ↓ │
│ 输出:ParsedBlock[](mdast AST) │
├─────────────────────────────────────────────────────────┤
│ BlockTransformer(可选中间件) │
│ - 打字机效果:sliceAst() 截断 AST │
│ - 维护 TextChunk[] 实现渐入动画 │
│ - 可跳过,直接渲染完整内容 │
│ ↓ │
│ 输出:DisplayBlock[](截断后的 AST) │
├─────────────────────────────────────────────────────────┤
│ Vue / React 组件渲染 │
│ - AST 节点 → 组件映射 │
│ - TextChunks 包装渐入动画 │
└─────────────────────────────────────────────────────────┘
↓
Vue / React DOM
关键特点:
- 增量解析:只解析新增的稳定块
- 打字机动画在 AST 节点层操作
- 同时提供 Vue 和 React 适配器
核心差异
| 维度 | ant-design-x | markstream-vue | Incremark |
|---|---|---|---|
| 解析方式 | 全量解析 (marked.js) | 全量解析 (markdown-it) | 增量解析 (micromark) |
| 单次 chunk 复杂度 | O(n) | O(n) | O(k),k = 新块大小 |
| 总复杂度 | O(n × chunks) ≈ O(n²) | O(n × chunks) ≈ O(n²) | O(n) |
| 边界处理 | 正则 token 检测 | 预处理层 | 稳定边界检测 |
| 打字机动画 | 文本字符串层 | Vue Transition | 保持 Markdown 结构 |
| 输出格式 | HTML 字符串 | 自定义 AST | mdast(兼容 remark) |
| 框架支持 | React | Vue | Vue + React |
解析策略
全量解析 vs 增量解析
全量解析(ant-design-x 和 markstream-vue):
Chunk 1: "# Hello" → 解析全部内容
Chunk 2: "# Hello\nWorld" → 再次解析全部内容
Chunk 3: "# Hello\nWorld\n\n- item" → 再次解析全部内容
每个新 chunk 都触发对所有累积内容的完整解析。
增量解析(Incremark):
Chunk 1: "# Hello" → 解析 → Block 1 (heading) ✓ 完成
Chunk 2: "\n\nWorld" → 解析 → Block 2 (paragraph) ← 只处理这部分
Chunk 3: "\n\n- item" → 解析 → Block 3 (list) ← 只处理这部分
已完成的块被缓存,不会重新解析。只处理待定部分。
复杂度分析
在多 chunk 流式场景中:
- 全量解析:O(n) × chunk 数量
- 增量解析:每个 chunk O(k),k 是新块大小
对于典型的 AI 响应(10-50 块),两种方案都能接受。差异在大文档或高频 chunk 到达时更明显。
流式边界处理
所有方案都需要处理流式过程中不完整的 Markdown 语法,各自采用不同方案:
| 方案 | 工作原理 | 权衡 |
|---|---|---|
| Incremark | 解析前检测稳定边界 | 结构清晰;可能缓冲部分内容 |
| ant-design-x | 正则模式检测不完整 token | 立即输出;需要维护正则 |
| markstream-vue | 解析前预处理内容 | 适用于任何解析器;需处理多种边界情况 |
打字机动画
| 方案 | 层级 | 机制 |
|---|---|---|
| Incremark | AST 节点 |
sliceAst() 截断 AST,TextChunk[] 追踪渐入 |
| ant-design-x | 文本字符串 | 逐字符文本切片 |
| markstream-vue | 组件 | 组件挂载时 Vue <transition>
|
各方案的权衡:
- AST 层:动画过程中保持结构感知
- 文本层:更简单,与框架无关
- 组件层:与 Vue 响应式系统自然集成
渲染优化
markstream-vue 提供额外的渲染优化:
| 特性 | 描述 |
|---|---|
| 虚拟化 | 只在 DOM 中渲染可见节点 |
| 批量渲染 | 使用 requestIdleCallback 渐进式渲染 |
| 视口优先 | 延迟渲染屏幕外节点 |
流式场景的考量:
在典型 AI 流式用例中:
- 内容逐步到达(天然的批量处理)
- 用户注意力在底部(观看新内容)
- 典型响应大小是 10-50 块
- 打字机效果提供渐进式渲染
虚拟化在以下场景提供显著收益:
- 浏览历史内容(非活跃流式)
- 渲染超长文档(100+ 块)
- 用户快速滚动浏览内容
总结
各方案的侧重点
ant-design-x
侧重:完整的 AI 聊天 UI 解决方案
- 提供 Bubble、Sender、Conversations 组件
- 与 Ant Design 生态深度集成
- 内置
<thinking>等特殊块支持 - Ant Design 用户可快速上手
适用场景:在 Ant Design 生态中构建 AI 聊天界面的团队
markstream-vue
侧重:功能丰富的 Markdown 渲染
- 虚拟化处理大文档
- 自适应性能的批量渲染
- 全面的边界情况预处理
- 丰富的自定义选项
适用场景:有大文档或聊天历史浏览需求的 Vue 应用
Incremark
侧重:高效的增量解析
- 增量解析:已完成的块不会重新解析,在长流式会话中显著减少 CPU 消耗
- 跨框架:同一套核心库支持 Vue 和 React,降低多框架团队的维护成本
- 兼容 remark 生态:标准 mdast 输出,可使用 remark 插件扩展语法
- 结构化打字机:动画过程中保持 Markdown 结构,插件系统支持自定义行为(如图片立即显示)
适用场景:长流式内容、多框架团队、或需要 remark 插件兼容的应用
快速参考
| 你的优先级 | 可考虑 |
|---|---|
| Ant Design 生态集成 | ant-design-x |
| 大文档虚拟化 | markstream-vue |
| 纯 Vue 应用 | markstream-vue |
| 长流式会话 / 多 chunk | Incremark |
| Vue + React 共用代码 | Incremark |
| 需要 remark 插件 | Incremark |
结论
每个方案以不同的优先级解决流式 Markdown:
- ant-design-x 提供与 Ant Design 紧密集成的完整 AI 聊天 UI 解决方案
- markstream-vue 为 Vue 应用提供丰富功能和渲染优化
- Incremark 专注于解析效率和跨框架灵活性
选择取决于你的具体需求:生态契合度、文档规模、框架需求和性能优先级。