Markdown 是一组纯文本标记语法,而不是只有一个渲染器、功能完全一致的单一语言。John Gruber 的原始设计强调可读源码和简单 HTML 输出;CommonMark 规定兼容核心,GitHub Flavored Markdown(GFM)、MDX、R Markdown 和各编辑器方言则增加不同能力。可靠的文档应声明方言、渲染器、扩展、消毒策略和目标输出。

核心要点

  • 先学习 CommonMark,再核对目标平台方言。表格、任务列表、脚注、数学公式、Mermaid、原始 HTML 和 JSX 并非所有 Markdown 的核心能力。
  • Markdown 源码不会自动变成安全 HTML。应对输出进行转义或消毒,限制 URL 协议、属性、嵌入内容和原始 HTML。
  • HTML 与 Markdown 互转可能丢失结构、样式、元数据、空白或行为,应保留原始来源并测试往返。
  • 使用语义标题、描述性链接、有意义的替代文本、可键盘操作的 HTML,避免依赖颜色或布局技巧传达信息。
  • 代码围栏、表格对齐、脚注和换行依赖解析器细节,应使用生产环境的精确渲染器测试示例。

方言与兼容性

方言或扩展 它是什么 常见增加能力
CommonMark 规定核心语法和解析模型 标题、段落、强调、列表、链接、图片、引用、代码、分隔线
GFM GitHub 的扩展集合 表格、任务列表、删除线、自动链接和代码围栏行为
MDX Markdown 与 JSX/组件语法结合 需要受信任构建流水线的组件和表达式
R Markdown 加入可执行文档工具的 Markdown 代码块、输出和可复现报告流程
数学/Mermaid 扩展 渲染器集成 公式或图表区块

“支持 Markdown”不足以说明兼容性,必须写出解析器和启用的扩展。GFM 能渲染的文档,在只支持 CommonMark 的解析器中可能只显示原始标点。

CommonMark 核心语法

标题与段落

ATX 标题使用一到六个 #,后跟空格:

markdown
## 二级标题
### 三级标题

Setext 标题使用下划线,只支持一、二级:

markdown
一级标题
===========

二级标题
-------

段落之间使用空行。段落中的单个换行在渲染后可能仍是空格;硬换行可在行尾使用两个空格或渲染器支持的反斜杠,显式 <br> 属于 HTML,可能被禁用或消毒。

标题层级应表达文档结构,而不是只为了获得更小字号。在宿主文档允许时保留一个有意义的页面主标题,不要只为视觉尺寸插入低级标题。

强调与行内代码

markdown
*斜体*_斜体_
**粗体**__粗体__
***粗斜体***
`行内代码`
\*字面星号\*

分隔符的行为会受标点和嵌套影响。命令、路径和标识符应使用反引号;代码本身包含反引号时使用更长的围栏。

列表与引用

markdown
- 第一个项目
- 第二个项目
  - 嵌套项目

1. 第一步
2. 第二步

> 一段引用。
>
> > 嵌套引用。

有序列表的数字可能被渲染器规范化;如果读者会维护源码,应使用有意义的编号。保持缩进一致,并测试嵌套代码块。

链接与图片

markdown
[CommonMark 规范](https://spec.commonmark.org/)

[长引用链接][spec]

[spec]: https://spec.commonmark.org/ "CommonMark"

![展示测试结果的终端截图](./test-result.png)

链接文字应描述目标,而不是“点击这里”。优先使用 https 并校验协议;除非有明确策略,否则拒绝 javascript:、意外的 data: 和不安全自定义协议。

图片替代文本应表达图片用途或重要信息。只有真正装饰性的图片才使用空 alt;图表、流程图或仅图片说明应提供文本替代。图片 URL 还可能涉及版权、留存和隐私审查。

分隔线

一行中的三个或更多相同 -*_ 可以形成分隔线:

markdown
---

不要用分隔线替代缺失的标题或语义段落。

代码、表格与脚注

围栏代码

使用带可选语言标签的围栏:

markdown
```python
print("hello")
```

标签只是高亮提示,不保证代码可编译,也不保证每个渲染器认识它。不要依赖颜色表达含义;代码应在无高亮时仍可读。伪代码应明确标注,要求可复现的命令或依赖应固定版本。

表格

很多平台把表格作为 GFM 扩展:

markdown
| 字段 | 含义 |
| :--- | :--- |
| `id` | 稳定标识符 |
| `name` | 展示名称 |

单元格中的字面管道符应转义或放进代码,表头要有意义;表格过宽或包含屏幕阅读器难以理解的重要关系时,应提供线性替代。不要只用表格做页面布局。

脚注

部分解析器支持脚注扩展:

markdown
一条证据说明[^source]。

[^source]: 写明来源和访问日期。

应检查生成的 ID、键盘导航、返回链接和本地化。目标渲染器不支持脚注时,改用普通链接或明确的参考资料区。

原始 HTML 与安全渲染

很多解析器会传递部分 HTML:

html
<details>
<summary>实现说明</summary>
内容仍受宿主 HTML 策略约束。
</details>

原始 HTML 不是安全边界。接受不可信 Markdown 的服务应使用明确的解析器,对生成 HTML 按允许列表消毒,移除事件处理属性和危险 URL,限制 iframe/img/svg 行为,并使用合适的 Content Security Policy。不要执行不可信文档中的脚本、JSX 或模板表达式。

align、内联样式和任意尺寸可能被忽略或删除。优先使用宿主应用拥有的语义 HTML 和 CSS。不要声称 Markdown 解析器本身能阻止 XSS。

数学公式、Mermaid 与其他扩展

$...$$$...$$ 数学分隔符、Mermaid、语法高亮和自定义指令都是渲染器能力。应定义:

  • 解析器和扩展版本;
  • 公式是否转换为可访问 MathML,是否提供文本替代;
  • 图表在服务端还是客户端渲染;
  • 图表文字、链接和 SVG 输出是否消毒;
  • 不支持扩展的读者和导出格式使用什么回退。

Mermaid 源码不自动等于图片,不应默认从不可信输入执行或渲染。图表不要只依赖颜色,应包含标签和文字说明。

转换边界

HTML 与 Markdown 的表达能力不同。转换可能丢失 CSS 选择器、ARIA 关系、嵌套结构、注释、ID、脚注、数学公式、交互行为和精确空白;HTML 还可能包含转换器必须移除的不安全元素。

更可靠的转换流程是:

  1. 确认来源信任级别并保留原始文件;
  2. 使用理解规范的库,而不是正则表达式;
  3. 明确映射标题、列表、链接、图片、表格和代码;
  4. 按目标上下文消毒输出;
  5. 用正常和畸形输入 fixture 渲染并比较;
  6. 记录解析器版本、选项、源哈希、输出哈希和复核结果。

Markdown 转 HTML 的输出仍需转义和消毒;HTML 转 Markdown 也不能证明语义或视觉等价。

无障碍与发布

  • 使用逻辑标题层级和描述性链接。
  • 为视觉内容提供 alt、图注、文字稿或数据表。
  • 保持键盘焦点可见,确保折叠和控件无需鼠标也能操作。
  • 不要只用颜色传达状态。
  • 检查对比度、代码可读性、表格溢出、缩放/重排和减少动画行为。
  • 保留语言元数据,使用能帮助读者和工具的代码标签。

Markdown 本身不能保证无障碍;渲染器、CSS、HTML 结构、内容和辅助技术路径都影响结果。

编写检查清单

  1. 声明目标方言和渲染器。
  2. 需要移植时优先使用核心语法。
  3. 在目标环境测试链接、图片、代码围栏、表格、脚注和扩展。
  4. 发布不可信 Markdown 前先校验并消毒。
  5. 保留源文件、许可证、图片权利和转换元数据。
  6. 在 CI 中执行链接、无障碍和渲染输出检查。
  7. 审查导出 HTML 的脚本、URL、ARIA 结构和折叠行为。

常见问题

Markdown 有统一标准吗?

没有一个覆盖所有实现的 Markdown 语言。CommonMark 规定核心,GFM 和其他工具增加扩展。需要兼容时应写明方言和渲染器。

Markdown 文件渲染安全吗?

不会自动安全。原始 HTML、URL、图片、SVG、扩展和生成 HTML 都可能带来安全或隐私风险,应根据目标上下文解析和消毒。

Markdown 与 HTML 可以无损互转吗?

通常不能。交互行为、样式、元数据、空白、脚注、公式和无障碍关系都可能丢失。应限制转换子集、测试并保留事实来源。

围栏代码的语言标签会执行代码吗?

不会,通常只是高亮提示。但构建系统或 notebook 扩展可能增加执行行为,必须检查流水线后再把文档当作代码。

为什么表格或脚注在一个站点有效,在另一个站点无效?

这些功能属于扩展,或解析器规则不同。应检查目标方言、插件、版本和生成的 HTML。

一手来源

总结

Markdown 的价值在于源码可移植、可检查,而不是所有渲染器行为相同。应选择并记录方言,让语义优先于装饰,消毒不可信输出,保留无障碍信息,并用实际发布流水线测试转换。这样速查表才不会变成兼容性或安全陷阱。