Markdown 项目别再混着看:remark、rehype 和 ByteMD 的真实分工
大家好,我是若风。
前几天我在看一类 Markdown 项目,本来只是想顺手梳理一下编辑器选型,结果越看越觉得,很多人其实把几个完全不同层级的东西混在一起了。
看到 remark,说这是 Markdown 渲染器。看到 rehype,说这也是 Markdown 插件。看到 ByteMD,又说这不就是另一个 Markdown 编辑器吗?
说实话,这种混淆挺正常。因为它们都出现在 Markdown 工具链里,名字又都短,README 里也经常同时出现 plugin、processor、editor 这些词。读 3 个项目很快,真正麻烦的是把它们放回同一张工程地图里。
表面上看,它们都在处理 .md。真正关键是,它们站的位置完全不一样。
如果只用一句话概括:
remark 和 rehype 是内容处理管线,ByteMD 是面向用户的编辑器组件。前者关心 AST 怎么流动,后者关心人怎么写、怎么预览、怎么扩展。
先把位置摆清楚
可以先看一张表。
| 项目 | 核心对象 | 处理阶段 | 典型用途 |
|---|---|---|---|
| remark | Markdown AST,也就是 mdast | Markdown 解析、检查、改写、再输出 | GFM、frontmatter、lint、目录生成、Markdown 到 HTML |
| rehype | HTML AST,也就是 hast | HTML 解析、检查、改写、再输出 | sanitize、slug、代码高亮、HTML 压缩、HTML 到组件 |
| ByteMD | 编辑器和预览 UI | 用户输入、编辑、预览、插件集成 | 在 React、Vue、Svelte、Angular 里嵌入 Markdown 编辑器 |
这张表基本就够用了。我倒是觉得,先把这 3 个位置摆清楚,比一上来追 API 更重要。
remark 解决的是「Markdown 作为结构化数据怎么处理」。rehype 解决的是「HTML 作为结构化数据怎么处理」。ByteMD 解决的是「给用户一个可用、可扩展、能预览的 Markdown 编辑器」。
这三个项目可以在同一个产品里合作,但它们不是同一种东西。
remark:它不是编辑器,而是 Markdown 管线
remark 官方对自己的定义很直接:一个由插件驱动的 Markdown processor,属于 unified collective。
这句话听起来有点抽象,翻译成人话就是:它把 Markdown 解析成一棵树,然后让插件在这棵树上做事。
比如一段 Markdown:
## Hello *Markdown*
在 remark 里,它不会只被当成一段字符串,而会变成类似这样的结构:
{
"type": "heading",
"depth": 2,
"children": [
{ "type": "text", "value": "Hello " },
{ "type": "emphasis", "children": [{ "type": "text", "value": "Markdown" }] }
]
}
这就是 mdast 的价值。你想把所有一级标题降成二级标题,想自动生成目录,想检查链接,想支持 GFM 表格,想读取 frontmatter,本质上都是在这棵树上做转换。字符串时代会让你焦虑的正则边界,到了 AST 时代会清爽很多。
所以 remark 的重点不是「把 Markdown 展示出来」,而是给 Markdown 建一条可编排的处理流水线。
这也是为什么 Astro、MDX、Docusaurus、Next.js 博客生态里经常能看到 remark-* 插件。它们通常在内容还处于 Markdown 阶段时动手。比如:
remark-gfm:支持 GitHub Flavored Markdownremark-frontmatter:识别 YAML frontmatterremark-toc:生成目录remark-rehype:把 Markdown AST 转成 HTML AST
注意最后这个点。remark-rehype 是桥。
它把 Markdown 世界接到 HTML 世界。
rehype:它接管的是 HTML 阶段
rehype 和 remark 很像,但对象换了。
remark 处理 Markdown AST,rehype 处理 HTML AST。
如果说 remark 站在内容源头,关心「作者写了什么 Markdown」,那 rehype 更靠近渲染结果,关心「最终 HTML 应该长什么样」。
比如一篇文章已经从 Markdown 变成了 HTML 结构:
<h2>Hello Markdown</h2>
<p>Visit <a href="https://example.com">example</a>.</p>
进入 rehype 以后,插件可以做很多渲染层面的事:
- 给标题加
id,方便锚点跳转 - 给外链加
target="_blank"和rel - 清理不安全 HTML,避免 XSS
- 给代码块做高亮
- 压缩 HTML
- 把 HTML AST 转成 React、Vue、Svelte 等组件
这就是 hast 的价值。
很多人第一次接触 remark / rehype 会觉得绕:为什么 Markdown 不能一步到 HTML?为什么要分 mdast 和 hast?
原因其实很工程化。Markdown 和 HTML 的问题域不一样。坦白讲,这也是我后来对 unified 生态改观的地方,它不是为了显得高级才引入 AST,而是在给插件复用留边界。
Markdown 阶段适合处理作者意图,比如标题层级、列表、引用、frontmatter、任务列表。HTML 阶段适合处理页面结构,比如链接属性、元素安全、代码高亮、组件输出。两者硬揉在一起,插件会变得很难复用,也很容易在错误阶段做错误的事。
我之前在这个博客里修过一个类似问题:想让一段 Markdown 内容不渲染,最稳的方案不是等到 HTML 阶段再删,而是在 remark 阶段直接删掉 AST 范围。因为等到 rehype 阶段,很多 Markdown 内容早就被拆成独立节点了。
这就是两个阶段的边界感。
ByteMD:它不是普通渲染器,而是编辑器产品底座
再看 ByteMD。
ByteMD 的定位就完全不同了。它是一个 Markdown editor component,核心是编辑器和预览器,而不是底层语法树处理库。
官方 README 里有几个关键词很重要:
- built with Svelte,但可以用于 React、Vue、Angular 等框架
- 轻量,编译后不依赖某个 UI 框架运行时
- 有插件系统,可以扩展代码高亮、数学公式、Mermaid、GFM 等能力
- 默认处理 XSS 风险
- 支持 SSR
这意味着 ByteMD 解决的是产品层问题。
普通的 Markdown 开源项目,常见有三类:
第一类是 parser / renderer。它关心 Markdown 怎么变成 HTML,比如 markdown-it、marked 这类项目。
第二类是 processor / plugin ecosystem。它关心内容怎么在 AST 管线里被解析、检查、改写,比如 remark 和 rehype。
第三类是 editor component。它关心用户怎么输入、怎么预览、怎么插插件、怎么嵌进你的业务系统。ByteMD 属于这一类。
所以 ByteMD 和「普通 md 开源项目」最大的不同,不在于它也支持 Markdown,而在于它把 Markdown 能力包装成了一个可嵌入的编辑体验。你不是拿到一把螺丝刀,而是拿到一块能直接塞进后台系统的编辑区域。
一个普通 Markdown parser 给你的通常是一个函数:
const html = render(markdown)
ByteMD 给你的更像是一块产品积木:
<Editor value={value} plugins={plugins} onChange={setValue} />
<Viewer value={value} plugins={plugins} />
差异就在这里。
parser 处理文本,processor 处理树,editor 处理人。
ByteMD 比普通 Markdown 编辑器多了什么
ByteMD 还有一个值得注意的点:它不是只做一个「textarea + preview」。
它把几个工程问题一起打包了。
第一,是跨框架包装。
ByteMD 本体用 Svelte 构建,但提供了 React、Vue 2、Vue 3 等包。对业务团队来说,这很实用。你不用因为项目是 React 就重写一套编辑器逻辑,也不用因为迁移 Vue 版本就完全换工具。
第二,是插件模型。
Markdown 编辑器真正麻烦的地方,从来不是基础语法,而是业务扩展。代码高亮、公式、Mermaid、frontmatter、GFM、emoji、图片上传、自定义工具栏,这些才是长期维护成本。
ByteMD 的插件系统把这些能力放在一个统一入口里。你不是到处 patch 编辑器,而是围绕插件扩展。
第三,是安全默认值。
Markdown 编辑器如果允许预览 HTML,就绕不开 XSS。ByteMD 明确把 <script>、<img onerror> 这类风险纳入默认处理。对一个要嵌进后台、CMS、知识库的编辑器来说,这不是锦上添花,而是底线。
第四,是 SSR 兼容。
很多 Markdown 编辑器默认假设自己只跑在浏览器里,一碰到 SSR 就要加动态导入、关闭服务端渲染,最后项目里多出一堆奇怪判断。ByteMD 把 SSR 当作目标之一,说明它不是玩具级组件,而是面向真实应用集成。
不过也要注意一个现实:pd4d10/bytemd 当前 README 已经标明这是 ByteMD v1 repository,v2 正在 HashMD 下开发。也就是说,选型时不能只看 ByteMD 这个名字,还要看你要的是稳定 v1,还是愿意跟进 HashMD 的后续路线。
真正的选择题:你到底要解决哪一层问题
如果你在做一个博客系统,选择路径大概是这样的。
你只是要把 Markdown 渲染成 HTML,可以先看 markdown-it、marked,或者直接用框架内置能力。
你要对 Markdown 做结构化处理,比如自动补目录、改写图片路径、检查链接、处理 frontmatter,那就看 remark。
你要处理最终 HTML,比如 sanitize、标题锚点、代码高亮、外链属性,那就看 rehype。
你要给用户一个编辑器,让他能写 Markdown、实时预览、插入插件、集成到 React / Vue 后台,那就看 ByteMD 或 HashMD 这一类 editor component。
很多技术选型踩坑,问题不在工具不好,而在把层级选错了。
你想修水管,却买了一套餐桌。
最后说两句
回到这三个项目。
截至我写这篇文章时,GitHub 上的数据大致是:
remarkjs/remark:约 8.9k stars,Markdown processor powered by pluginsrehypejs/rehype:约 2.2k stars,HTML processor powered by pluginspd4d10/bytemd:约 1.3k stars,ByteMD v1 repositorypd4d10/hashmd:约 4.3k stars,ByteMD v2 方向的 Hackable Markdown Editor and Viewer
这组数据也挺有意思。
remark 的影响力更像基础设施,因为它被大量内容系统、文档系统、MDX 工具链间接使用。rehype 的存在感稍低,但它在 HTML 阶段非常关键。ByteMD / HashMD 则更靠近最终用户体验,它的价值不是生态底层,而是把这些能力包装成一个能用、能嵌、能扩展的编辑器。
所以,下次再看到 Markdown 开源项目,可以先问一个问题:
它是在处理文本,处理树,还是处理人的编辑体验?
这个问题一问,很多项目的差异就清楚了。
总结一下,remark 是 Markdown 的结构化处理层,rehype 是 HTML 的结构化处理层,ByteMD / HashMD 是把 Markdown 能力产品化的编辑器层。
选型时别急着问「哪个更强」。先问自己一句:我现在卡住的是文本转换、AST 管线,还是用户编辑体验?
答案一变,工具就会跟着变。
评论互动