Markdown 转 HTML

粘贴 Markdown 即时得到符合标准的 HTML,可切换自动链接、智能标点、硬换行与原始 HTML 透传。

输入 Markdown,输出符合标准的 HTML。 输入即转换。四个开关会改变解析器行为,下方预览区同时呈现渲染效果,方便你一次核对源码与结果。

解析选项

预览

Markdown、CommonMark 与各种方言

为什么两个解析器会对同一份文档产生分歧

Markdown 于 2004 年发布,形态是一个 Perl 脚本加一页说明文字,没有形式化语法。这种随意性既是它得以流行的原因,也是同一份文档在不同工具里渲染结果不同的原因。原始实现留下了数十处未定义行为:缩进三个空格的列表标记该怎么处理、词内嵌套的强调如何解析、标题前是否必须有空行。每个实现者都靠猜测填补这些空白,而且猜得各不相同。

CommonMark 就是为弥合这个鸿沟而生的。它是一份精确的规范,配有数百个一致性测试,覆盖的正是原始版本留下的模糊场景。本转换器使用 markdown-it,它完整通过 CommonMark 测试套件,因此输出与 GitHub、Reddit、Discourse 以及大多数现代工具一致。在这里能正确渲染的文档,在那些平台上也会一样。

GitHub Flavored Markdown 是在其之上叠加的超集:表格、删除线、任务列表和 URL 自动链接。本工具支持表格和任务列表,示例文档里两者都有。这个区分在你为不可控的目标平台写作时很重要——表格并不属于 CommonMark 本体,严格的解析器会把它原样当作普通文本输出。

四个开关到底改变了什么

自动链接会把裸 URL 变成超链接。写变更日志和笔记时很方便,但它偏离了 CommonMark——后者要求裸链接必须用尖括号包裹。如果输出需要在严格解析器之间移植,请关掉它。

智能标点会把直引号改写成弯引号,把 -- 转成短破折号。这对散文是对的,但对代码块之外出现的代码片段就是错的,它会毫不客气地把命令行参数改坏。行内代码和围栏代码块始终不受影响。

硬换行改变单个换行的含义。默认情况下 Markdown 会把连续行合并成一个段落,这符合大多数文档的预期。但聊天式输入——用户按回车就是想换行——必须打开这个开关,否则排版会全部塌掉。

原始 HTML 透传是唯一有安全后果的一项。关闭时,源码里的 <img onerror=...> 会被转义成文本显示;打开时,它会作为可执行标记输出。任何用户提交的内容都应保持关闭。

消毒是另一件事

Markdown 流水线最常见的误解,是以为转换器会保护你。它不会,也不该负责这件事。即便关闭了原始 HTML,链接目标仍然由攻击者控制,[点我](javascript:alert(1)) 会生成一个浏览器可能执行的超链接。仅凭 Markdown 语法本身就足以构造出可用的攻击。

正确的做法是:先转换,再消毒,最后插入。用 DOMPurify 之类的专用消毒库配合标签与属性白名单处理 HTML,然后才放进页面。如果内容会展示给作者以外的人,这一步必须在服务端做——客户端消毒可以被任何能直接调用你 API 的人绕过。本工具是在你自己的浏览器里用你自己的输入生成预览,与发布他人 Markdown 属于完全不同的威胁模型。

开源说明:渲染由 markdown-it 提供,遵循 MIT 许可证。

常见问题

这里遵循的是哪个 Markdown 方言?
CommonMark,外加 markdown-it 默认启用的 GitHub 扩展,如表格和任务列表。它完整通过 CommonMark 一致性测试,因此输出与 GitHub、Discourse 及多数现代渲染器一致。
生成的 HTML 可以直接发布吗?
不可以。插入页面前务必先用 DOMPurify 之类的消毒库处理,尤其是非你本人撰写的内容。即使关闭了原始 HTML,链接目标仍受攻击者控制,javascript: 协议的 URL 可能溜过去。
为什么我的单行换行消失了?
标准 Markdown 会把连续行合并为一个段落,硬换行需要行尾两个空格或一个空行。如果你的源文是聊天式文本、每次回车都应换行,请打开「单个换行转为 <br>」。
可以直接在 Markdown 里粘贴 HTML 吗?
可以,前提是打开「允许原始 HTML」。否则标签会被转义并按字面文本显示,这是更安全的默认值。Markdown 中混写原始 HTML 是合法的,但块级标签前后需要空行才能按预期解析。
我的内容会被上传吗?
不会。markdown-it 完全在你的浏览器中运行,页面不会为你的文本发起任何网络请求。加载页面后断网也能继续转换。
为什么智能标点把我的代码弄坏了?
它会在正文范围内改写引号和连字符,所以写在代码片段之外的 --dry-run 会变成短破折号。请用反引号包住代码片段,解析器绝不会在行内代码或围栏代码块内做标点替换。