说明

尽管基础的 markdown 语法很简单,但是由于解析器实现的不一致性,尤其是对于扩展的 markdown 语法的支持程度差异很大,导致我们在使用 markdown 的时候会出现不同场景的差异性。这篇文章打算以 GFM 和 Typora 为主,去介绍一下 markdown 的语法格式规范以及各个解析器的支持程度。

本文中涉及到的所有的 markdown 语法我都会尽量注明各种情况是否支持(没有特别指出的时候,默认都支持),同时也会加一些markdownlint的规则描述。

  • GFM:GitHub Flavored Markdown,算是最通用的 markdown 扩展规范了;
  • Typora:个人认为最好的 markdown 可视化编辑器和查看器,支持windowsmac,在 GFM 基础上,它还支持很多额外的 markdown 扩展;
  • kramdown:作为jekyll的默认 markdown 解析器,现在应该是 github pages 默认的 markdown 解析器;
  • visual studio code的 markdown 扩展:可以方便启用markdownlint检查,尽管该解析器还存在较多问题,也会关注下其解析效果;

基础语法

标题

使用#字符就可以指定标题,输入 1-6 个#,分别对应 1-6 级标题。顾名思义,#数目越少,标题级别就越高。#和标题名之间应该确保包含一个空格,另外标题前后需要存在一行空行。

# 一级标题

## 二级标题

### 三级标题

###### 六级标题

在生成html时,会对应到<h1><h2><h3><h6>

<h1>一级标题</h1>
<h2>二级标题</h2>
<h3>三级标题</h3>
<h6>六级标题</h6>

正文段落

普通文本可以直接输入,需要注意的是普通的换行是不生效的(也有部分 markdown 解析器会生效),只是方便在编辑器中查看而已。如果要实现换行的话,需要在行尾加上两个空格,或者手动加上<br/>标签(不符合 markdown lint 标准,非必要情况下不要这么操作)。
我在上面末尾加了两个空格,所以这是新的一行(但不是新的一段)。

如果中间有一行空行的话,则会产生是一个新的段落。换行对应到 html 的<br/>,段落对应到 html<p> </p>

行首和行尾的单个空格一般会被忽略,段落之间不要有多行空行。

字体样式

字体样式 语法 例子 html
粗体(Bold) ** **__ __ 这是粗体 <strong></strong>
斜体(Italic) * *_ _ 这是斜体 <em></em>
粗斜体(Bold and Italic) *** ***___ ___ 这是粗斜体 <strong><em></em></strong>
删除线(Strikethrough) ~~ ~~ 这是删除线 <del></del>

删除线不属于标准的 markdown 规范,但是一般的解析器都会支持。至于使用*还是使用_则看个人习惯,使用*的人更多一些。

引用

使用>即可进行引用,对应 html 的<blockquote></blockquote>,如果引用需要分多段的的话,可以在中间加一个仅包含>的一行即可。如果中间是一个空行的情况,大部分解析器会将其认为是 2 个引用块,也有部分会将其认为是 1 个引用块,使用中最好尽量避免中间有空行的情况出现。

李白曾经写过

君不见,黄河之水天上来,奔流到海不复回。君不见,高堂明镜悲白发,朝如青丝暮成雪。

人生得意须尽欢,莫使金樽空对月。天生我材必有用,千金散尽还复来。

列表

使用*-+产生无序列表,使用数字加.产生有序列表,有序列表的数字不一定需要有序,但是需要从 1 开始,也可以所有都是 1。

无序列表:

  • Java
  • C/C++
  • Python

有序列表:

  1. Java
  2. C/C++
  3. Python
  4. Go
  5. PHP

无序列表产生的 html 如下<ul><li></li><li></li></ul>,有序列表产生的 html 如下<ol><li></li><li></li></ol>

对于多级列表,需要确保子列表与上一级列表的内容对齐,对于有序列表的二级列表缩进 3 个空格(数字+.+空格),对于无序列表的二级列表缩进 2 个空格(-+空格)。对于 list 内部包含段落、引用、代码块、表格等情况,建议和上述规则保持一致。对于上述情况在二级列表或者段落前后包含一个空行。

不同的 markdown 解析器对于列表内包含段落的情况处理的并不一致,所以存在着各种写法,比如段落前用 4 个空格、在上一行末尾加入 2 个空格(软换行)等等,建议保持标准的写法兼容尽量多的解析器。

1. Java

   Java 连续霸榜 TIBOE 编程语言排行榜,已然成为业界最受欢迎的变成语言。

2. C/C++

   - C
   - C++

3. Python
4. PHP

   > 曾经有人说 PHP 是世界上最好的语言

   LAMP 曾经是标配,如今已经不再那么流行……
  1. Java

    Java 连续霸榜 TIBOE 编程语言排行榜,已然成为业界最受欢迎的变成语言。

  2. C/C++

    • C
    • C++
  3. Python
  4. PHP

    曾经有人说 PHP 是世界上最好的语言

    LAMP 曾经是标配,如今已经不再那么流行……

链接

链接写法 [展现名](链接地址 "标题"),比如腾讯网,其中的"标题"可以省略,链接地址可以使用绝对路径也可以使用相对路径,或者指向本地/本网站的其他文件。更进一步,也可以指向本文件的其他锚点(书签),比如跳转引用

展现的名字和链接地址一致的时候会显得有些冗余,比如[https://www.qq.com](https://www.qq.com),有更简洁的写法,直接用<https://www.qq.com>生成https://www.qq.com

如果多处需要链接相同的地址,我们也可以采用引用的方式,使用[展现名][链接名],然后在任意地方定义链接名的具体地址 [链接名]: 链接地址,比如腾讯,这样子还有个好处是方便管理。

图片

和链接的语法类似,图片需要在前面加上!,常见的写法![展现名][图片地址],如果需要图片本身也是链接的话,在外层加上链接地址:[![展现名][图片地址]](链接地址)

腾讯网图片

表格

表格不是标准的 markdown,但是常见的解析器会支持,写法如下,完整的情况会在第一列前面和最后列后面加上|,表头和内容中间需要一行分隔符,这里建议保持和列数一致,某些解析器也会支持最简单的写法---|---(不管多少列的情况下),但是很多解析器并不支持,这里可以通过:来指定表格的对齐方向,左对齐、右对齐还是居中。

表头1|表头2|表头3 :---|:---:|---: 内容|内容|内容 内容|内容|内容
国家 面积 人口
中国 960 万平方公里 14 亿
美国 936 万平方公里 3 亿
俄罗斯 1709 万平方公里 1.4 亿

分隔符

使用---或者***作为分隔符


转义字符

由于一些符号在 markdown 中有了特殊含义,比如我们就是要输入*abc*,并不希望出现斜体的 abc,那么可以使用\作为转衣符,输入\*abc\*,结果为*abc*。

扩展语法

代码

行内使用`作为起始和终止符,比如printf,代码块使用位于独立行的```作为起始和终止

int main(int argc, char *argv[])
    cout << "hello world!" << endl;
    return 0;
}

任务列表

任务列表(Task List)在 github 中使用非常广泛,语法格式如下,使用x标识为已完成,未完成的情况中间需要包含空格。

- [x] 银行存款超过 1000 万
- [ ] 当上总经理
- [ ] 赢取白富美
  • 银行存款超过 1000 万
  • 当上总经理
  • 赢取白富美

数学公式

数学公式块,使用位于独立行的$$作为起始和终止,比如:

$$
f(x)=\sum^{\infty}_{n=0}\frac{f^{(n)}(a)}{n!}(x-a)^n
$$
\[f(x)=\sum^{\infty}_{n=0}\frac{f^{(n)}(a)}{n!}(x-a)^n\]

行内数学公式,直接使用$作为起始和终止:$\delta=b^2-4ac$(有些解析器需要使用$$作为起始和终止)。

html 标签

可以使用 html 标签实现 markdown 不支持的功能,正常情况下尽量避免使用 html 标签。

  1. 在表格内部需要换行的时候,可以加上<br/>标签;
  2. 需要指定样式的时候,可以加上类似<span style="color:red"> </span>标签,比如:我是红色
  3. 指定下划线,使用<u> </u>,比如:我是下划线

非通用语法

内容目录

使用[TOC]生成自动目录。Typora 支持该语法,GFM 不支持该语法。

时序图

使用js-sequence渲染

罗密欧->朱丽叶: 哈喽
朱丽叶-->罗密欧: 我想你了
罗密欧->>朱丽叶: 我也想你了

image-20201204112218341

gfm 不支持时序图,Typora 支持。

流程图

使用flowchart.js渲染

st=>start: 开始
cond=>condition: 有房有车
op1=>operation: 赢取白富美
op2=>operation: 走向人生巅峰
e=>end: 结束

st->cond
cond(yes)->op1->op2->e
cond(no)->e

image-20201204112431026

gfm 不支持流程图,Typora 支持。

mermaid 图

mermaid相比时序图和流程图来说,功能会更强大,支持时序图、流程图、UML 图、状态图、甘特图等,Typora 支持但是 GFM 不支持。目前来看,使用算是比较广泛了。官网提供了较丰富的例子,Mermaid Live Editor提供了在线编辑以及导出 SVG。

看下用mermaid来画时序图的例子:

sequenceDiagram
罗密欧->>朱丽叶: 哈喽
朱丽叶-->>罗密欧: 我想你了
罗密欧->>朱丽叶: 我也想你了

image-20201204112846477

脚标和上下标

可以使用语法 ​[^脚标A]: 这是脚标A来创建角标(需要使用代码块模式),在需要引用的地方使用[^脚标A]进行引用[^脚标a]。Typora 支持该语法,GFM 不支持该语法。

上标和下标没有一致的标准,在 Typora 中使用^文字^表示上标,使用~文字~表示下标,但我看并没有得到广泛支持。建议在 markdown 中尽量避免使用上下标,如果要保持兼容性的话采用 HTML 的<sup>文字</sup>展示上标,使用<sub>文字</sub>的方式展示下标。

YAML 头信息

在文件的头部使用使用独立行的---作为开始和终止,其中间部分会作为 metadata,并不会生成可视内容,这本来是jekyll的特殊格式,现在 Typora 也支持该语法(尽管不一定会生成 html 里面的 metadata)。

Github emoji

按文本形式输入类似:smile:,常见的可以直接输入,完整的可以参考列表。Typora 和 github 都支持,vscode 的相关插件不支持。

  • 人::boy::girl::man::woman::baby::older_woman::older_man::princess::cop::angel::couple::walking::runner::dancers:
  • 动物::cat::dog::pig::frog::cow::horse::snake::bird::mouse::wolf::monkey::camel:
  • 表情::smile::cry::confused::sob::joy::mask::worried::wink::relaxed::grin::kissing::open_mouth::heart_eyes:

参考资料

  1. Writing on GitHub/Basic writing and formatting syntax
  2. GitHub Flavored Markdown Spec
  3. Markdown Guide
  4. Markdown Tutorial