什么是Markdown
Markdown是一种用简单符号表示格式的轻量标记写法,由John Gruber在2004年设计,Aaron Swartz也参与了语法的讨论。它的设计目标是:即使不转换,原文本身也容易阅读。行首的#是标题,用两个星号包住的文字是粗体,行首的连字符是列表项。由于只是纯文本,任何编辑器都能写,版本管理工具能清楚地比较差异,贴进聊天窗口也不会乱。因此它被广泛用于README、开发文档、博客、笔记应用和维基。
本页用一段专门编写的小型转换程序把Markdown转成HTML。结果区可以在渲染预览和HTML源码之间切换,并显示字符数、行数、标题数、链接数,以及被判定为不安全而拦截的网址数量。
本工具支持的语法
下表中的每一项都已用本页的转换程序验证过。把"输入示例"一列直接粘贴进编辑框,就能看到结果。
| 语法 | 输入示例 | HTML | 备注 |
|---|---|---|---|
| 标题 | # 标题1 ~ ###### 标题6 | h1-h6 | #后面必须有空格;下划线式(=== / ---)也支持 |
| 粗体・斜体 | **粗体** *斜体* | strong, em | 也支持 __粗体__ 和 _斜体_(单词中间的下划线会被忽略) |
| 删除线 | ~~删除~~ | del | GFM扩展 |
| 列表 | - 项目 / 1. 项目 | ul, ol | 缩进两个以上空格即为嵌套;有序列表保留起始编号 |
| 任务列表 | - [x] 已完成 / - [ ] 未完成 | input[type=checkbox] | GFM扩展;预览中的复选框不可点击 |
| 表格 | | 列1 | 列2 | + |:--|--:| | table | GFM扩展;用冒号指定对齐方式 |
| 引用 | > 引用文字 | blockquote | 内部可嵌套列表和标题 |
| 代码 | `code` / ```语言 ... ``` | code, pre | 也支持 ~~~ 围栏;内容原样显示,不做转换 |
| 链接・图片 | [文字](网址) /  | a, img | 只保留 http、https、mailto、tel 和相对路径;可读取引号内的标题 |
| 自动链接 | https://example.com / <https://...> | a | 仅限 http 和 https |
| 分隔线・换行 | --- / 行尾两个空格 | hr, br | 紧贴在段落下一行的 --- 会被当作标题下划线 |
反斜杠转义也受支持,写\*会原样显示星号。嵌套最多八层,更深的部分不再转换,而是作为转义后的文字显示。只有表头行和分隔行的列数相同,才会被识别为表格。
不支持的有:引用式链接([文字][1])、脚注、定义列表、数学公式、Mermaid图表、缩进式代码块(请改用围栏)以及原始HTML块。这些都是各家渲染器本来就不一致的地方,凭猜测转换只会让预览和最终发布平台对不上。
CommonMark、GFM与最初的Markdown
最初的Markdown说明有许多含糊之处,同一段输入在不同实现里会得到不同结果。CommonMark就是为消除这种歧义而制定的规范,撰写本文时的最新版本是2024年1月28日发布的0.31.2。GFM(GitHub Flavored Markdown)在CommonMark基础上增加了这些扩展:
- 表格:用竖线和分隔行制作,原始语法中没有。
- 任务列表:
- [ ]和- [x]。 - 删除线:
~~文字~~。 - 自动链接:直接写网址就会变成链接。
- 标签过滤:
script、iframe等原始HTML标签会被中和。
本工具实现了表格、任务列表、删除线和自动链接。标签过滤则无需实现,因为它根本不会让原始HTML通过(见下文安全部分)。
同样的Markdown为什么显示不同
- 换行:在CommonMark中,段落内单个换行只相当于空格,要强制换行需要行尾两个以上空格或反斜杠。有些评论区和聊天工具会把每个换行都显示出来,编辑框上方的复选框可以重现这种行为。
- 原始HTML:多数渲染器允许部分HTML,但保留哪些标签和属性各不相同。本工具一律转义,当作文字显示。
- 嵌套列表缩进:有的渲染器2个空格就行,有的要4个。有序列表建议按标记宽度缩进3个以上空格。
- 标题后的空格:
#标题这种没有空格的写法,大多数解析器不认作标题,请写成# 标题。 - 表格列数:表头行与分隔行列数不同就不是表格。
按真实输出核对的两个例子
例1:带对齐的表格 输入包含两列表头、分隔行中的:--(左对齐)与--:(右对齐),以及两行数据。转换后表格外面有一层可滚动的容器,每个表头和数据单元格都带有text-align样式。
| 姓名 | 分数 |
|:--|--:|
| 小王 | 90 |
| 小李 | 85 |<div class="md-table-wrap"><table>
<thead><tr><th style="text-align:left">姓名</th><th style="text-align:right">分数</th></tr></thead>
<tbody>
<tr><td style="text-align:left">小王</td><td style="text-align:right">90</td></tr>
<tr><td style="text-align:left">小李</td><td style="text-align:right">85</td></tr>
</tbody></table></div>例2:嵌套列表 把"苹果"和"梨"缩进两个空格,它们就成为"水果"条目里的子列表。结果是两个顶层项目和两个嵌套项目。
- 水果
- 苹果
- 梨
- 蔬菜<ul>
<li>水果
<ul>
<li>苹果</li>
<li>梨</li>
</ul></li>
<li>蔬菜</li>
</ul>安全:为什么不能轻信转换后的HTML
Markdown允许在原文里夹杂HTML,所以把用户输入的Markdown直接变成页面HTML,很容易造成跨站脚本(XSS)漏洞。常见的攻击有三种:
- 脚本标签与事件属性:例如图片标签的
onerror属性里藏代码。 - 危险的网址协议:例如链接地址以
javascript:开头。 - 属性逃逸:在网址里放引号,提前闭合
href属性,再插入新的属性。
本工具在代码中对这三种情况都做了处理。它先把&、<、>和引号转成实体,所以输入中的原始HTML只会显示为文字,不会变成元素。链接和图片的地址只接受 http、https、mailto、tel,以及相对路径和锚点,其余一律替换为#,用来伪装协议的控制字符在判断前会被去掉。外部链接带有rel="noopener noreferrer nofollow"并在新标签页打开。预览区直接填入生成的HTML,所以这些步骤就是唯一的防线,没有使用另外的净化库。在"安全测试示例"中,javascript:链接和data:图片都被替换为#,"被拦截的网址"显示为2,而script标签和图片标签则以文字形式显示。
作为只给自己看的预览工具,这样已经够用,但它不是生产代码的范本。手写的转换程序可能被意想不到的组合绕过。如果你的网站要显示他人撰写的Markdown,请同时使用持续维护的Markdown库和专门的HTML净化库,并尽量配置内容安全策略(CSP)作为第二道防线。
常见错误
- 列表前没有空行:有些渲染器不会把紧贴上一段的内容识别成列表,请空一行。
- 代码围栏数量不一致:用三个反引号开头、两个结尾,后面的全部内容都会变成代码。代码里本身有三个反引号时,外层改用四个。
- 意外的强调:公式或文件名里的
*、_可能被当成斜体,请用反斜杠转义。 - 网址里的空格与括号:空格要写成
%20;成对的括号可以,不成对的括号请避免。 - 图片替代文字为空:
会让读屏用户和图片无法加载的环境失去信息。
局限与不适合使用的场合
这是一个覆盖核心语法和部分GFM扩展的简易转换器,并不能通过CommonMark的全部测试。较复杂的结构,比如嵌套列表里的代码块,结果可能与GitHub或你使用的静态站点生成器不同。使用外部地址的图片时,预览会让浏览器访问该服务器,对方可能因此看到你的IP地址。发布前请务必在实际的发布平台(GitHub、博客系统或笔记应用)上再预览一次。另外,密码和个人信息不要输入任何在线工具,即使本工具不会上传输入内容,这也是一个好习惯。
如何利用转换结果
- 一边在左侧写,一边检查标题层级有没有跳级(例如h1后面直接是h3)。
- 切到"HTML源码"标签查看转换结果,需要时点"复制HTML"。注意粘贴到的网站会按自己的规则净化HTML。
- 要放进README或维基的文档,请在目标平台的渲染器里确认表格、任务列表和换行是否一致。
- 检查字数用字数统计,比较两个版本用文本对比工具,统一英文大小写用大小写转换。
- 如果文档里要放JSON示例,可以先用JSON格式化工具整理好。
常见问题
可以复制转换后的HTML吗?
可以。在"HTML源码"标签里查看,点"复制HTML"即可。粘贴到其他网站时,该网站自己的净化规则决定哪些标签和属性会保留。
表格怎么写?
第一行写表头,格式为| 列1 | 列2 |,第二行写分隔行,如|--|--|,之后是数据行。分隔行里:--为左对齐,:-:居中,--:右对齐。每一行的列数都要和表头相同。
Markdown里可以直接写HTML标签吗?
在本工具中不行。出于安全考虑,HTML标签不会被渲染,而是作为文字显示。GitHub等平台会保留允许的标签并过滤其余部分,但各平台的范围不同,请在实际发布的平台上确认。
换行后内容还是连成一行怎么办?
在CommonMark中,单个换行不会产生新的一行。请在行尾加两个空格,或者用空行分段。也可以勾选编辑框上方的"把换行直接转为 <br>",看看对换行敏感的系统会怎样显示。
输入的内容会发送到服务器吗?
不会。转换由浏览器中的脚本完成,输入内容不会上传。唯一的例外是外部图片:如果Markdown里引用了外部图片地址,预览会为了加载图片而访问该地址。
该用Markdown还是HTML?
以文字为主的文档(README、博客、笔记)用Markdown更省事;需要精确排版、颜色或表单时用HTML更合适。许多平台允许在Markdown中夹杂少量HTML,但前提是其渲染器允许。