跳到主内容

← 返回文章列表

GitHub Flavored Markdown vs CommonMark:转换时该选哪个

给目标平台选错 Markdown 方言,转换在技术上成功了,结果却悄悄渲染错位:表格变成一串竖线文本,删除线露出波浪号,任务列表丢了复选框。修复不花钱,前提是转换前就选对。

本指南讲清楚 CommonMark 到底规范了什么、GFM 在它之上又加了什么、各平台各自说的是哪种方言,以及这个选择在转换器把文档写成最终文件的那一刻如何落地生效。

CommonMark:核心语法的一份规范

经典 Markdown 是一个 Perl 脚本加一段散文描述,到 2010 年代已有几十个实现各执一词:缩进段落之后列表要不要延续、跨行算不算强调、数字加点什么时候是列表。同一个文件处处渲染得不一样。

CommonMark 用一份正式规范作答:给核心语法无歧义的文法、数百个各有一个正确结果的测试样例、可供比对的参考实现。pandoc 作者 John MacFarlane 也在其中,这套规范很快站稳了脚跟。

如今 CommonMark 是几乎所有实现共同立足的基座。它刻意不包含表格和删除线语法,它定义的只是这些特性立足其上的地基,而这恰恰是第二层方言得以存在的全部理由所在。

GFM 在上面加了什么

GitHub Flavored Markdown 于 2017 年公开规范,是 CommonMark 的一个严格超集。新增了五件事:用竖线写的表格、双波浪号删除线、带复选框的任务列表、裸 URL 自动成链,以及拦截特定原生 HTML 标签的过滤器。

每一条都瞄准开发者本来就在写的东西。管道表格覆盖依赖矩阵和测试矩阵;任务列表把 README 变成清单;自动链接让人不必给每个 URL 套尖括号。HTML 过滤是安全措施,在人人可编辑的站点上剥掉让内嵌 HTML 危险的标签。

GitHub 用 cmark-gfm 渲染 Markdown,它是 CommonMark 参考实现的一个分支,把这些扩展直接接了进去。GitLab 和 Gitea 走的是同样的路线,所以 GFM 在各大代码托管平台上的表现基本一致,不会随站点漂移。

各自的方言主场

GFM 的主场显然在 GitHub,以及默认采用它的静态站点工具:Hugo 出厂就配置成 GFM 渲染,MkDocs 默认开启表格。Obsidian 对管道表格、删除线、任务列表照单全收,还加上自己的方言特性。

严格的 CommonMark 解析器住在库和博客引擎里,偏爱的是小而可预测的核心。pandoc 自己的默认 Markdown 是第三种方言,更大的超集,带网格表格、引用语法等扩展,GitHub 不会渲染这些。

这不说明哪种方言更好。问题从来不是哪种方言抽象意义上赢了,而是你的目的地实际渲染哪种方言,以及你即将产出的文件是否守在那个边界之内。

转换器为什么在意这个差别

转换器不只在翻译语法,还在选择产出哪种方言。表格是选择最可见的地方。目标是 GitHub 且输出方言支持管道表格时,简单表格落地还是表格;方言不支持时,同一张表会退回目的地不认识的语法,读者看到的是一排排带竖线的纯文本。

删除线和任务列表复选框以同样方式失败,只是动静更小:波浪号原样露出,复选框括号被字面渲染。文档没有坏,只是它说的语言读者的渲染器听不懂。

这也是为什么源格式没有想象中那么重要。pandoc 无论读 docx 还是 CSV,都先解析成抽象文档模型,方言决策发生在写出输出那一刻。一个 reader,多个 writer,而你真正控制的是 writer。

按目的地来选

凡是落在代码托管平台或笔记库里的东西,选 GFM:GitHub 的 PR、GitLab wiki、Obsidian 笔记、Hugo 站点都能渲染它的结构。当目标是严格的 CommonMark 引擎或博客库时,选纯 CommonMark,并接受表格退回 HTML 或扩展方案。

不确定目的地时,GFM 是更稳妥的默认选择。它的四个可见扩展是整个 Markdown 世界里支持面最广的,跳过某项的平台通常也能优雅降级,而不会裸露原始语法。

唯一值得主动避开的错误是假设超集永远安全。用 pandoc Markdown 的网格表格或引用语法写出的文件,在 pandoc 里完美无缺,到 GitHub 上就坏了。产出目标平台自己的方言,永远好过产出它解析不了的大超集。

转换时 GFM 开关改了什么

本站转换器把选择做成一个开关。开启 GFM 后,Markdown 输出用管道表格,删除线和任务列表语法原样保留,同时避开 GitHub 不渲染的构造,如 HTML 图片标签和链接宽度属性。

不开则走 pandoc 自己的 Markdown 风味:输出依然完全有效、也依然可读,但复杂表格很可能落进 GitHub 所忽略的方言里,CommonMark 核心之外的功能也不保证以大家熟悉的 GFM 形态落地。

这个开关还有一处安静的联动值得知道:CSV 和 TSV 输入的产物本身就是一张表格文档,是 GFM 模式让这张表保持 GitHub 真正渲染的管道语法,而不是它会忽略的变体。

换行这个边缘情况

换行是躲不开的另一个差别。CommonMark 里,段落内的单个换行是软换行,会被渲染成空格,想要硬换行需要行尾两个空格或一个反斜杠。GFM 对文件保持同样规则。

现实更混乱:GitHub 的评论和 issue 正文把单个换行当可见换行渲染,Obsidian 默认也一样,除非在设置里开启严格换行。于是同一段在句中断行的文字,在 GitHub 页面和旁边的 README 里是两种样子。

实用规则:输出是 CommonMark 或 GFM 时,段落写成单个长行;只在平台明确支持换行即断行时才依赖它。方言选择的其他一切事后能修,换行是唯一藏在明处的问题。

继续阅读

在浏览器里试试