一个真实项目的选型对比:从踩坑正则库到拥抱 AST,性能提升 100 倍。
痛点开局:博客系统里的 500 毫秒
前阵子给一个开源社区写 Wiki 插件。需求很简单:把用户存的 .md 文件渲染成网页。
第一版方案更简单。网上搜了个老教程,直接 Install-Package MarkdownSharp,20 行代码搞定。上线第三天,运维群里炸了:“某个 2 万字的架构设计文档打不开,页面转了 5 秒白屏。”
我一看代码,心跳漏了半拍。那个类库基于 正则表达式 递归解析,遇到超长文本和复杂嵌套列表直接飚到 400-500ms 响应耗时 。
有个坑: 你以为选个下载量高的库就稳了?NuGet 上排第一的 MarkdownSharp 虽然有 3200 万下载,但它其实是个“历史包袱”,性能比现代解析器慢 100 倍。具体数据我们后面看。
说白了,对 Markdown 解析这种场景,选错引擎比选错框架更致命。
正文:三个主流选项与致命伤
先给结论:如果你现在还在纠结用哪个,直接选 Markdig。它现在几乎是 .NET 生态的事实标准。
1. MarkdownSharp:老古董的叹息
这个是 Stack Overflow 早期用的移植版,纯 C# 实现,依赖正则 。
优点:不用学新 API,简单的文本转换一把梭。
致命伤:无 AST(抽象语法树) 。这意味着它一旦遇到复杂嵌套(比如列表套引用再套代码块),容易触发正则回溯灾难。
2. CommonMark.NET:曾经的优等生,现已停更
这是严格按照 CommonMark 规范(0.30 版本)实现的库,由 .NET 基金会维护过一段时间 。比 MarkdownSharp 规范,不会乱解析斜体。
然而它有个大问题:扩展性为 0。你想支持 GFM 任务列表(- [x])?不支持。想支持表格?默认不支持。而且这个项目已基本处于停更状态 。
3. Markdig:核弹级生产力
作者 Alexandre Mutel 写的,完全遵循 CommonMark 标准,通过了 600+ 官方测试用例 。
它不是简单的转换器,它先把 Markdown 解析成 AST(语法树),再渲染成 HTML。这个结构让拓展、修改、语法高亮、来回转换都变得极其优雅。
性能实测:Markdig 到底有多快?
我直接贴官方 benchmark 实测数据 :
| 方法 | 平均耗时 (Mean) | 性能对比 |
|---|---|---|
| markdig | 1.979 ms | 基准(最快) |
| cmark (C 参考实现) | 2.571 ms | 慢约 30% |
| CommonMark.NET | 2.016 ms | 略慢但合规 |
| MarkdownSharp | 221.455 ms | 慢 100 倍以上 |
你没看错,在处理复杂文档时,Markdig 甚至比 CommonMark 的 C 语言参考实现(cmark)还要快 20%。
实战代码:从 Hello World 到工业级配置
场景一:最简入门(几乎零配置)
默认使用的是严格 CommonMark 模式,不开启任何花哨的 GFM 扩展。
using Markdig;
var markdown = "# 标题 \n 这是一段带 *强调* 的文字。";
var html = Markdown.ToHtml(markdown);
Console.WriteLine(html);
// 输出: <h1>标题</h1>\n<p>这是一段带 <em>强调</em> 的文字。</p>
场景二:开启“黑科技”(管道模式)
这是 Markdig 最精髓的地方。你可以像搭积木一样配置解析器。
我们的 Wiki 系统需要支持:表格、任务列表、脚注、删除线,甚至 Mermaid 流程图。
using Markdig;
// 构建管道:使用 AdvancedExtensions 一次性开启 90% 的常用功能
var pipeline = new MarkdownPipelineBuilder()
.UseAdvancedExtensions() // 包含表格、任务列表、脚注等
.UseEmojiAndSmiley() // 支持 :smile: 转义
.UseMathematics() // 支持 LaTeX $数学公式$
.Build();
var complexMd = @"
# 功能演示
| 语法 | 支持 |
|------|------|
| 表格 | ✅ |
- [x] 已完成任务
这是一个脚注[^1]。
[^1]: 这是脚注内容。
";
var result = Markdown.ToHtml(complexMd, pipeline);
踩坑实录:XSS 攻击与代码高亮
坑 1:默认不防 XSS
Markdig 默认不转义 HTML 标签。如果用户输入包含 <script>,会被直接输出到页面。
解决方案:渲染前启用 DisableHtml 扩展。
var safePipeline = new MarkdownPipelineBuilder()
.DisableHtml() // 关键!这会忽略原始 HTML 标签
.UseAdvancedExtensions()
.Build();
坑 2:代码块高亮需要自己接入
Markdig 本身不携带 Prism 或 Highlight.js 的 CSS/JS 逻辑,它只负责生成 <code> 标签并标注 language-csharp。
正确做法:后端渲染出带 lang 属性的代码块,前端配合 highlight.js 初始化。
// Markdig 渲染结果示意
// 输入: ```csharp\nvar x = 1;\n
// 输出: <pre><code class="language-csharp">var x = 1;</code></pre>
```
总结:我为什么放弃其他选型
Markdig 赢在哪?
- 快:20% 快过 C 语言实现 。
- 活:管道模式让它像中间件一样可配置 。
- 新:持续跟新 CommonMark 标准 (0.31.2) 。
如果你的业务涉及论坛、CMS、文档中心,或者需要做复杂的 Markdown 编辑预览,直接上 Markdig,别再重复造轮子了。


