GoForum🌐 V2EX

今天发布了 Incremark 0.3.0:双引擎架构 + 完整插件生态, AI 流式渲染的终极方案

1244943563 · 2026-01-08 10:43 · 0 次点赞 · 0 条回复

Incremark 0.3.0 发布:双引擎架构 + 完整插件生态,AI 流式渲染的终极方案

距离上次发帖已经过了一段时间,这段时间我一直在打磨 incremark ,今天正式发布 0.3.0 版本。这次更新的核心是双引擎架构——你可以在极速的 marked 和稳定的 micromark 之间自由切换,同时享受完整的插件生态。

先说结论

md 来源:本地随机抽取的 38 个文件,最大只有 18kb ,文件更大的时候,这个差距只会随指数拉大,没有专门写测试 markdown 文件,就拉了文档中的一些 md 以及使用 cursor 时候生成的一些文档。可查看文档看详细数据 详细数据

对比方案 平均优势 最大差距
vs Streamdown 6.1 倍 16.4x
vs ant-design-x 7.2 倍 18.9x
vs markstream-vue 28.3 倍 65.6x

基于 38 个真实 markdown 文档( 6,484 行,128.55 KB )的基准测试结果。

在线体验:

为什么要做双引擎?

上次发帖后收到很多反馈,主要集中在两个方向:

  1. “能不能更快?” —— 性能党
  2. “能不能支持 xxx 语法?” —— 功能党

这两个需求看似矛盾,但我找到了一个优雅的解决方案:双引擎架构

Marked 引擎(默认)—— 极速派

// 默认使用 marked ,可以省略
<IncremarkContent 
  :content="content" 
  :is-finished="isFinished"
  :incremark-options="{ engine: 'marked' }"
/>

特点:

  • 🚀 极速:针对流式场景深度优化
  • 🔧 自研扩展:我们为 marked 补齐了脚注、数学公式、自定义容器、内联 HTML 解析
  • Tree-shaking 友好:只打包你用到的功能

Micromark 引擎 —— 稳定派

<IncremarkContent 
  :content="content" 
  :is-finished="isFinished"
  :incremark-options="{ engine: 'micromark' }"
/>

特点:

  • 严格 CommonMark:100% 规范兼容
  • 🔌 丰富生态:直接使用 micromark/mdast 社区的所有插件
  • 🛡️ 稳定可靠:久经考验的解析器

Tree-shaking 优化

为了打包体积,我们做了 tree-shaking 优化:

// 默认只打包 marked (极速模式)
import { createIncremarkParser } from '@incremark/core'
const parser = createIncremarkParser({ gfm: true })

// 如果需要 micromark ,单独导入
import { MicromarkAstBuilder } from '@incremark/core/engines/micromark'
const parser = createIncremarkParser({
  astBuilder: MicromarkAstBuilder,
  gfm: true
})

这样你的项目只会打包你实际使用的引擎,不用担心 bundle 体积问题。

我们为 Marked 做了什么增强?

原生 marked 不支持很多 AI 场景常用的语法,我们通过自研扩展补齐了这些能力:

功能 原生 Marked Incremark Marked Streamdown
脚注 ✅ 完整 GFM 脚注
数学公式 $...$$$...$$ ⚠️ 部分
自定义容器 :::tip:::warning
内联 HTML 解析 ⚠️ 基础 ✅ 完整 HTML 树 ⚠️ 基础

这解释了为什么在某些基准测试中 Incremark 看起来”更慢”——因为我们在做更多的事情

文件 Incremark Streamdown 说明
footnotes.md 1.7 ms 0.2 ms Streamdown 跳过了脚注解析
FOOTNOTE_FIX_SUMMARY.md 22.7 ms 0.5 ms 同上

这是功能差异,不是性能问题。 Streamdown 跳过不支持的语法所以看起来更快,而 Incremark 完整解析了所有内容。

完整基准测试数据

为了让大家心里有底,我把 38 个测试文件的完整数据都放出来:

文件 行数 大小 Incremark Streamdown ant-design-x markstream-vue
introduction.md 34 1.57 KB 5.6 ms 4.5 ms 12.8 ms 57.5 ms
quick-start.md 71 3.14 KB 12.8 ms 26.7 ms 37.9 ms 214.2 ms
concepts.md 91 4.38 KB 12.0 ms 50.5 ms 51.5 ms 287.5 ms
comparison.md 109 5.39 KB 20.5 ms 94.9 ms 85.2 ms 418.9 ms
OPTIMIZATION_SUMMARY.md 391 8.90 KB 19.1 ms 208.4 ms 340.3 ms 1685.7 ms
BLOCK_TRANSFORMER.md 489 9.24 KB 75.7 ms 320.9 ms 619.9 ms 2268.7 ms
test-md-01.md 916 17.67 KB 87.7 ms 1441.1 ms 1656.9 ms 7927.9 ms

规律很明显:文档越长,Incremark 的优势越大。 这就是 O(n) vs O(n²) 的威力。

核心架构一览

┌─────────────────────────────────────────────────────────────────┐
│                      IncremarkContent                           │
│  (声明式组件,处理 content/stream 输入)                          │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                      IncremarkParser                            │
│  ┌─────────────────────────────────────────────────────────────┐
│  │              双引擎 AST 构建器                                │
│  │  ┌──────────────────┐    ┌──────────────────┐               │
│  │  │  MarkedAstBuilder│    │MicromarkAstBuilder│              │
│  │  │  (默认,极速)     │    │ (稳定,严格)      │               │
│  │  └──────────────────┘    └──────────────────┘               │
│  └─────────────────────────────────────────────────────────────┘
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                     BlockTransformer                            │
│  (打字机效果,字符级增量渲染)                                     │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐          │
│  │     Vue      │  │    React     │  │    Svelte    │          │
│  │    组件库     │  │    组件库     │  │    组件库    │          │
│  └──────────────┘  └──────────────┘  └──────────────┘          │
└─────────────────────────────────────────────────────────────────┘

最简使用方式

我们推荐使用 IncremarkContent 组件,这是最简单的方式:

<script setup>
import { ref } from 'vue'
import { IncremarkContent } from '@incremark/vue'

const content = ref('')
const isFinished = ref(false)

// 处理 AI 流式输出
async function handleStream(stream) {
  content.value = ''
  isFinished.value = false
  
  for await (const chunk of stream) {
    content.value += chunk
  }
  
  isFinished.value = true
}
</script>

<template>
  <IncremarkContent 
    :content="content" 
    :is-finished="isFinished"
    :incremark-options="{ gfm: true, math: true }"
  />
</template>

新增 Svelte 支持

应社区要求,我们新增了 Svelte 5 支持:

pnpm add @incremark/svelte
<script lang="ts">
import { IncremarkContent } from '@incremark/svelte'

let content = $state('')
let isFinished = $state(false)
</script>

<IncremarkContent {content} {isFinished} />

致 V2EX 社区

说实话,这个项目的 star 大部分应该都是 V2EX 的朋友们贡献的。上次发帖后收到了很多有价值的反馈:

  • @Leon6868 提的淡入动画 → 已实现 effect: 'fade-in'
  • @weareoutman 提的 memo render → 我们的 BlockTransformer 就是干这个的
  • 还有很多关于性能、兼容性的讨论

感谢大家的支持和反馈,让这个项目越来越好。

资源链接


如果觉得有用,欢迎 star ⭐️

有任何问题或建议,欢迎在评论区讨论,或者直接提 issue 。

0 条回复
添加回复
你还需要 登录 后发表回复

登录后可发帖和回复

登录 注册
主题信息
作者: 1244943563
发布: 2026-01-08
点赞: 0
回复: 0