FlowingDev

Markdown 详解:纯文本是如何披上斗篷的

学习 Markdown 是如何使用星号、井号等简单符号,将纯文本变成格式精美的文档、网页和消息的。

试用工具: Markdown 查看器

一句话概括

Markdown 是一种语法,它让你能用简单易读的标点符号,而不是复杂的代码或笨重的按钮,来写出富文本格式(比如加粗、列表和链接)。

它解决了什么问题

让咱们把时间倒回到 21 世纪初。如果你想在网上写点东西,基本上只有两个糟糕的选择。选择 A:手写原生 HTML。这意味着你得手动敲 <p>、<strong>、<ul>、<li> 和一大堆别的标签。这不仅慢、容易出错,还让你的源文本看起来像机器人打的喷嚏。选择 B:使用“所见即所得”(WYSIWYG)编辑器,就像早期博客平台或微软 Word 里的“另存为 HTML”功能那样。这些编辑器以吐出臃肿、混乱且不标准的 HTML 而臭名昭著,这些 HTML 经常会以各种神秘的方式崩溃。

这两种选择对真正的写作者来说,都不友好。

2004 年,作家 John Gruber 在已故的 Aaron Swartz 的帮助下,创造了 Markdown 来解决这个困境。他们的核心理念在当时相当激进:一份文档的纯文本原始版本,本身就应该尽可能地易于阅读,不应该有任何格式化标签碍事。其目标不是要取代 HTML,而是创造一种“写作优先”的语法,可以轻松地被转换成干净的 HTML。

你不再需要写 <strong>快看这个!</strong>,只要写 **快看这个!** 就行了。你不再需要为列表写一堆乱糟糟的 <ul> 和 <li> 标签,只需使用星号。它的设计理念是“人类优先,计算机其次”。这使得它非常适合用来写博客文章、评论、论坛,尤其是项目文档。

底层工作原理

当你在编辑器里输入 Markdown,并在旁边看到漂亮的预览时,你其实正在见证一场两步舞:解析(parsing)和渲染(rendering)。所谓的“Markdown 查看器”或“编辑器”,就是一个实时表演这场舞蹈的工具。

解析器:从符号到结构

第一步是解析。一个叫做解析器(parser)的程序会从上到下读取你的纯文本文件。它不只是在读单词,它在寻找定义了 Markdown 语法的那些特殊字符。

  • 当它在一行的开头看到 ## 我的绝妙点子,它会想:“啊哈!这不只是普通文本,这是一个二级标题。”
  • 当它看到以 * 开头的一行,它会识别出这是一个列表项的开始。
  • 当它发现被双星号包围的文本,比如 **这个**,它会将其标记为“强力强调”(即加粗)。

在这个过程中,解析器并不会直接生成 HTML。相反,它通常会构建一个文档结构的内部表示,这通常被称为抽象语法树(Abstract Syntax Tree, AST)。可以把它想象成一张蓝图。这张蓝图里没有 <h2> 标签,而是有一个“标题”节点,其“级别”为 2,内容是“我的绝妙点子”。

下面是这个过程的简化版:

你的 Markdown:

## Shopping List

- Milk
- **Important**: Bread

简化的 AST(蓝图):

Document
└── Heading (level 2, content: "Shopping List")
└── UnorderedList
    ├── ListItem (content: "Milk")
    └── ListItem
        └── Text (content: " ")
        └── Strong (content: "Important")
        └── Text (content: ": Bread")

渲染器:从结构到 HTML

一旦解析器构建好了 AST 这张蓝图,渲染器(renderer)就接管了工作。渲染器的任务是遍历这棵树形结构,并将每个节点转换成它的最终格式,通常是 HTML。

  • 它看到“标题”节点(2 级),就打印出 <h2>Shopping List</h2>。
  • 它看到“无序列表”节点,就在其内容两端加上 <ul> 和 </ul>。
  • 它找到“列表项”节点,就用 <li> 和 </li> 将其包裹起来。
  • 它看到“强力强调”节点,就用 <strong> 和 </strong> 将其内容包裹起来。

最终生成的 HTML:

<h2>Shopping List</h2>
<ul>
<li>Milk</li>
<li><strong>Important</strong>: Bread</li>
</ul>

然后,这段干净的 HTML 会被交给浏览器(或其他任何显示最终输出的程序),由它来渲染出你实际看到的格式化文本。

方言和扩展(“CommonMark”妥协案)

Gruber 最初的规范在一些地方有点模糊。如果你在一个块引用里再放一个列表,而这个块引用本身又在另一个列表里,会发生什么?不同的解析器会给出不同的答案。这导致了 Markdown “方言”的兴起,每种方言都有自己的一些小调整和扩展。

功能 原始 Markdown GitHub 风格 Markdown (GFM)
表格 不支持 支持
删除线 (~~text~~) 不支持 支持
任务列表 (- [x]) 不支持 支持
围栏代码块 (``````) 不支持 支持

目前为止,最流行的方言是 GitHub 风格 Markdown (GFM),它为开发者协作添加了一些关键功能,如表格、语法高亮的代码块和任务列表。方言的泛滥也带来了它自己的问题:你写的文本在 GitHub 上和在 Stack Overflow 上的渲染效果可能不一样。

为了解决这个问题,一群开发者发起了 CommonMark 计划,这是一个旨在为 Markdown 创建一个高度详细、无歧义规范的项目。现在大多数现代的 Markdown 解析器都以兼容 CommonMark 为目标,而 GFM 则是其一个流行的超集。

真实世界的故事

一份拯救了项目的 README

一位开发者,我们姑且叫她 Priya,加入了一个新团队。项目代码库很复杂,最初的作者们早已离职。就在她开始感到恐慌时,她找到了它:项目根目录下的 README.md。它不仅仅是一个文件,更是一根救命稻草。它用清晰的标题解释了项目的目的。一个“快速开始”章节用有序列表一步步地指导了完整的安装步骤。关键的命令被放在了语法高亮完美的代码块中。甚至还有一个“故障排查”章节,列出了常见的错误和解决方案。Priya 在一个小时内就在自己的机器上把项目跑起来了,而不是好几天。

这个故事的启示: README.md 文件里的 Markdown 是帮助开发者上手、让项目易于理解的独一无二的最有效工具。它的简洁性鼓励开发者去真正地编写和维护文档。

那个抛弃了所见即所得编辑器的博主

Alex 经营着一个技术博客,但他非常讨厌他的内容管理系统(CMS)内置的编辑器。它不仅慢,粘贴代码片段简直是一场格式灾难,而且它生成的 HTML 也是一团糟。后来 Alex 发现了 Markdown,醍醐灌顶。他开始在本地一个简单、无干扰的文本编辑器里写所有文章。文本很干净,代码块很完美,而且因为它只是一个 .md 文件,所以可以用 Git 进行备份。当一篇文章准备好后,他只需把原始的 Markdown 复制粘贴到他的 CMS 里(谢天谢地,他的 CMS 支持 Markdown 输入模式)。他的写作速度变快了,挫败感减少了,而且他的内容现在完全是可移植的,不再被锁定在某一个平台上。

这个故事的启示: Markdown 将你的内容与表现形式解耦。通过使用一种通用的纯文本格式来写作,你就真正拥有了自己的作品,并且可以轻松地在不同工具和平台之间迁移。

非开发人员的 Pull Request

一家小型创业公司的市场团队在对外公开的 API 文档网站上发现了一个明显的拼写错误。这份文档托管在 GitHub 上,所有文件都是 Markdown 格式。一位产品经理,完全不懂 HTML 或 Git,却能轻松地在 GitHub 网站上找到那个文件,点击“编辑”按钮,然后看到了人类可读的 Markdown 文本。她修正了拼写错误,加了一条评论来解释她的修改,然后点击了“提议更改”。这个操作创建了一个 pull request,一位开发者很快就审查并合并了它。几分钟之内,修复就上线了。

这个故事的启示: Markdown 的可读性降低了协作的门槛。它让非技术团队成员也能够直接为文档、网站等做出贡献,而无需先成为一名开发者。

常见错误和陷阱

  • 忘记加空行。 这是导致“为啥我的列表没渲染出来?!”的头号元凶。许多 Markdown 元素,比如列表、块引用和代码块,都需要在它们前面有一个空行才能被正确解析。你的肉眼可能看到了一个列表,但解析器需要那个空行来切换上下文。

  • 列表缩进不一致。 当创建子列表时,你用来缩进的空格数量很重要。CommonMark 规范指出,2 或 4 个空格的缩进是标准做法。混用制表符和空格,或者使用不一致的缩进,都会破坏列表的结构。

  • 想当然地认为你的方言是通用的。 你用 GFM 的管道符语法(| Head | Head |)精心制作了一个漂亮的表格,然后把它粘贴到一个只支持原生 Markdown 的系统里。结果:一堆乱码般的管道符和破折号。永远要清楚你的目标平台支持哪种方言。

  • 换行不等于段落。 在源文件里,你按一次回车键来换到下一行。但在渲染出的结果里,这通常不会创建一个新段落,而只是把两行连接起来。要创建一个真正的段落(<p> 标签),你需要一个完整的空行(即按两次回车)。要强制进行一次简单的换行(<br> 标签),可以在一行的末尾输入两个空格,然后再按回车。

  • 没有对特殊字符进行转义。 想写出 *literally* 这个字面文本,而不是让它变成斜体?你需要用反斜杠来“转义”这个特殊字符:\*literally\*。这条规则同样适用于 #、_、[、] 以及其他有语法含义的字符。

为什么它值得你关注

无论何时,当你需要书写一种易于编写、易于阅读且不被专有格式锁定的格式化文本时,你都应该想到 Markdown。它是开发者之间沟通的通用语。

  • 项目文档: 每一个 README.md、CONTRIBUTING.md 和维基页面。
  • 笔记记录: 像 Obsidian、Joplin 和 Bear 这样的工具都是基于 Markdown 构建的,让你能创建一个可移植、可链接的个人知识库。
  • 内容创作: 为静态网站生成器(如 Jekyll、Hugo、Eleventy)或“无头” CMS 写作。
  • 日常交流: 在 GitHub/GitLab 上写 issue、pull request 和评论;在 Stack Overflow 上提问和回答;在 Slack 或 Discord 里聊天。

Markdown 恰好命中了 .txt 的过于简陋和 .docx 或原生 HTML 的过度复杂之间的那个“甜蜜点”。它是现代软件开发和数字通信的基础工具。

深入探索

理论搞定,动手试试吧——100% 在你的浏览器中运行。

试用工具: Markdown 查看器