
Hello World
Hello World 是 Hexo 创建时就会生成的一篇文章,标题沿用到现在,在很久之前就标记为草稿不再构建。到现如今更新了,将其作为渲染管线的自检页:Markdown 写进来之后不会直接输出,@content-collections 先解析 Frontmatter,mdx-bundler 把正文编译成 MDX,中间依次挂上 remark-gfm、rehype-slug 和 rehype-mermaid,最后由 Next.js 静态渲染,代码块交给 Shiki。
下面按语法类别,把这条管线能遇到的写法过一遍,顺便记下每一条当前的实际表现。每次换插件或者调配置,都会回到这里看看哪一段的输出变了。
另外,有些写法虽然合法,但会被仓库的 prettier 改写成别的形式,本文不测这些写法,原因列在文末。
一、标题
标题由 Frontmatter 的 title 渲染,正文本来不该再写一级标题。这里把 H1 到 H6 全列出来,只是为了确认组件映射没有漏掉哪一级。
一级标题 H1
锚点不会支持 H1
二级标题 H2
三级标题 H3
四级标题 H4
五级标题 H5
六级标题 H6
每个标题的 id 由 rehype-slug 生成,页面上的锚点靠它。
Setext 标题
Setext 一级标题
即使是 Setext 标题,一级也不支持锚点
Setext 一级标题
==============Setext 二级标题
标题边界
####### 七个井号不是标准标题
#标题后没有空格时通常不是标题
二、段落与换行
段落之间空一行,这一点没有例外。只按一次回车算软换行,渲染后会和上一行接在同一段里:
这句话末尾只按了一次回车。 它和上一句属于同一个段落。
硬换行有行尾两个空格和行尾反斜杠两种写法。前者看不见那两个空格,很容易被编辑器或格式化工具清掉;本文统一用反斜杠:
这一行末尾是反斜杠
同样断开了。
三、强调
星号和下划线等价,叠三个就是粗斜体,GFM 另外补了删除线。
- 双星号加粗
- 双下划线加粗
- 单星号斜体
- 单下划线斜体
- 三星号粗斜体
- 三下划线粗斜体
删除线行内代码
混排时只要不越界,各写各的:
- 加粗里放
代码和 斜体 - 斜体里放 加粗 和
删除线 - 中文紧贴着加粗,英文写成 wordinsideword,用来确认单词内部的星号不会乱咬
- 行内代码里的标记一律不生效:
**not bold** _not italic_ [not link](#) - 想在里面再放反引号,外壳用两个:
Use `code` inside inline code
四、引用
这是第一层引用,可以连着写很多行。 中间不空行,它们就属于同一个引用块。
引用里也能用加粗、斜体 和
代码。
嵌套就是再套一层尖括号:
第一层
第二层
第三层
引用里放列表和代码块也没问题:
- 引用中的无序列表
- 第二项
tsconst quoted = true
高亮块
本站给引用加了一个标记:块首写一行 [!HIGHLIGHT color=...],构建时 formatHighlights 会认出它,把这行删掉,并给 blockquote 挂上 data-highlight 属性。颜色可选 yellow、blue、green、red、purple,不写就是蓝色。
这是一个蓝色高亮块,里面照常能用 Markdown。
- 可以放列表
- 也可以放链接
不写颜色,默认蓝色。
颜色也接受六位十六进制值。这时属性会写成 data-highlight-color="custom",具体颜色通过 CSS 变量传进去:
这是一个自定义颜色的高亮块。
标记行和正文之间要留一行空的 >,让它们成为两个独立段落。忘了留,[!HIGHLIGHT] 会和正文挤成一段,直接显示在页面上。不认得这个约定的渲染器会把它退化成普通引用块,颜色没了,文字还在。
五、列表
无序列表嵌套
- 一级项目 A
- 一级项目 B
- 二级项目 B-1
- 二级项目 B-2
- 三级项目 B-2-a
- 三级项目 B-2-b
- 一级项目 C
有序列表
- 第一步
- 第二步
- 第三步
- 第三步的子项一
- 第三步的子项二
- 第四步
起始编号
- 从 7 开始
- 自动往下排
- 这里写的 9,因为会被格式化,但即使不格式化写 10 也应该显示 9
有序与无序混排
- 准备工作
- 检查环境
- 安装依赖
- 执行
- 运行脚本
- 子步骤一
- 子步骤二
- 运行脚本
列表项里的段落和代码
-
第一段。
同一个列表项里的第二段,缩进要和文字对齐。
-
带代码块的列表项:
bashnpm run build
任务列表
- 待办事项
- 已完成的事项
- 带子任务的事项
- 子任务已完成
- 子任务未完成
复选框由 GFM 生成,本站渲染时会带上 disabled,所以只能看,不能点。GFM 也认大写 X,但 prettier 会统一成小写,本文只用小写。
六、代码
行内代码用反引号包住,里面的 **、[] 都不会被解析。
围栏代码块的语言标识决定 Shiki 用哪套高亮。目前装了 asm、bash、c、go、http、ini、javascript、json、protobuf、ruby、sql、ssh-config、toml、tsx、typescript、vue 和 yaml,另外认 ts、js、sh、yml、proto 这类别名。写别的语言不会报错,只是按纯文本处理。
function greet(name: string) {
const message = `Hello, ${name}!`
console.log(message)
return message
}
greet('World')package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}在语言标识后面跟一个文件名,代码块顶部就会显示它。这一步由 formatCode 完成,它把围栏的 meta 直接塞进 code 元素的 filename 属性:
export const post = z.object({
title: z.string(),
category: z.string(),
keywords: z.array(z.string()).optional(),
})顶部那一栏里,文件名居中,语言在右上角,旁边是复制按钮。
缩进四个空格也能生成代码块,只是没法指定语言:
四个空格缩进也可以形成代码块 const indented = true;
围栏也能用波浪线代替反引号,效果一样,但 prettier 会统一成反引号,本文只用反引号:
{
"name": "vacuum",
"markdown": true
}七、链接与图片
链接
图片
正文图片由 Image 组件渲染,默认懒加载,并关掉拖拽:

标准语法可以加上标题  但是没有实现,或许后面考虑实现它。
引用式写法可以省掉重复的长 URL:

标准 Markdown 没有尺寸语法,要指定显示大小只能写成 <img>,width 和 height 按像素算,不过我的实现中已经指定了 width: auto,所以并不能通过这种方式指定。
八、表格
基础表格
| 语言 | 用途 | 特点 |
|---|---|---|
| Python | 数据分析与脚本 | 语法简洁,生态丰富 |
| Rust | 系统与高性能服务 | 内存安全,没有垃圾回收 |
| TypeScript | 前端与全栈开发 | 给 JavaScript 补上静态类型 |
| Go | 云原生与后端服务 | 并发模型简单,编译快 |
列对齐
| 左对齐 | 居中对齐 | 右对齐 |
|---|---|---|
| left | center | right |
| 文本 | 文本 | 文本 |
| a | bb | ccc |
单元格里的格式
| 类型 | 示例 | 备注 |
|---|---|---|
| 加粗 | bold | 行内语法 |
| 代码 | const x = 1 | 行内代码 |
| 链接 | example | 单元格链接 |
| 转义竖线 | a | b | 显示一条竖线 |
九、分割线
分割线写作三个及以上连字符:
星号和下划线也能当分割线,但 prettier 会统一成 ---,本文只演示这一种。
十、转义字符
反斜杠能把特殊字符还原成普通字符:
- *不会变成斜体*
- _不会变成斜体_
- # 不会变成标题
- `不会变成代码`
- [不会变成链接](https://example.com)
- !不会变成图片
- > 不会变成引用
- - 不会变成列表
- + 不会变成列表
- \1. 不会变成有序列表
- 反斜杠本身写作
\\
十一、解析边界
列表还是年份
数字加句点会被当成有序列表,年份得转义:
1986. 这是一年,不是列表项。
- 这才是列表项
列表项后面的懒续行仍然属于同一个列表项,空行才会把它断开:
-
第一行 这是同一个列表项的续行。
这是同一个列表项里的第二个段落。
-
下一个列表项
围栏嵌套
外层用四个反引号,里面的三个就不会提前收尾:
```ts
const nestedFence = true
```链接目标的边界
空单元格
| 左空 | 中空 | 右空 |
|---|---|---|
| B | C | |
| A | C | |
| A | B |
十二、GFM 扩展
删除线
这段文字被划掉了。
自动链接
链接和邮箱不用写成 Markdown 链接也能识别:访问 https://github.com,或者发邮件到 hi@example.com。
尖括号那种写法在 MDX 里要当心,<https://example.com> 可能被当成 JSX 标签,直接用上面的字面量形式更省事。
脚注
GFM 的脚注在这里可用,正文里的角标会跳到文末的定义。1
定义列表
定义列表不属于 CommonMark,也不在 GFM 里,本站没有对应插件,下面两行会原样显示成普通段落:
术语 : 定义内容
十三、HTML、实体与 MDX
HTML 块与行内标签
MDX 允许直接写 HTML,它会当作 JSX 处理:
展开
段落里有 HTML 加粗 和 行内代码。
行内标签也能和 Markdown 混排:按 Command + K,以及 高亮文本。
实体与尖括号
- 命名实体:
&→ &,<→ <,©→ © - 数字实体:
#→ #,😀→ 😀 - GFM 自动链接字面量:https://example.com/path?q=markdown&lang=zh
- 转义之后只是普通文本:<not-a-tag>content</not-a-tag>
花括号
Markdown 里花括号就是普通字符,MDX 会把它当成表达式,想显示原文得转义:{ key: "value" }。代码里的花括号不受影响:const value = { nested: true }。
十四、图表
Mermaid 交给 rehype-mermaid 处理,构建时用 Playwright 渲染成内联 SVG,页面里不需要额外的脚本。CI 专门装 Chromium 就是为这一步。
流程图
时序图
甘特图
饼图
思维导图
思维导图没有单独语法,还是写在 mermaid 代码块里,用缩进表示层级,root(( )) 是中心主题:
括号形状对应节点形状:(( )) 圆形、( ) 圆角、[ ] 方形、{{ }} 六边形。
十五、没有测试的写法
下面这些写法本身合法,解析器大概率也认得,但仓库用 prettier 统一 Markdown 格式,pnpm lint 里的 format/prettier 会把它们改写成另一种形式。为了不和格式化结果打架,本文直接不写这些形式:
- 行尾两个空格的硬换行:prettier 会删掉行尾空白,硬换行统一写成反斜杠。
- 无序列表的
*和+标记:prettier 可能把它们改成-,本文统一用减号。 - 任务列表里的大写
[X]:会被改成小写。 - 波浪线围栏
~~~:会被换成反引号。 ***和___分割线:会被换成---。- 链接目标里不加尖括号的括号,以及空目标
():prettier 会补上或改写成尖括号。 - 省略首尾竖线的表格:prettier 会补全竖线。
这一页只收录格式化之后仍然稳定的写法,所以它同时是一份渲染测试和一份格式基线。想确认上面这些形式在渲染器里是什么表现,不要改这篇文章,另开一个临时文件,用 <!-- prettier-ignore --> 或者单独跑一遍构建再验。
十六、综合混排
提示: 引用里的列表混了
代码、链接 和删除线。
- 第一项有 加粗
- 第二项有一段行内代码:
const x = 1;- 第三项引出下面的表格
| 功能 | 状态 | 说明 |
|---|---|---|
| 标题 | 是 | H1-H6,rehype-slug 生成锚点 |
| 段落与换行 | 是 | 软换行合并,行尾两空格或反斜杠断开 |
| 强调 | 是 | 加粗、斜体、粗斜体、删除线 |
| 引用 | 是 | 支持嵌套、列表和代码块 |
| 高亮块 | 本站扩展 | [!HIGHLIGHT] 标记,别的渲染器退化成普通引用 |
| 列表 | 是 | 有序、无序、任务列表,嵌套任意层 |
| 任务列表 | 只读 | 复选框由 GFM 生成,渲染时禁用 |
| 表格 | 是 | GFM,支持对齐、转义竖线、省略首尾竖线 |
| 代码块 | 是 | Shiki 高亮,语言和文件名写在围栏上 |
| 链接与图片 | 是 | 行内、引用式,<img> 可指定尺寸 |
| 脚注 | 是 | GFM 脚注,定义排到文末 |
| 定义列表 | 否 | 非 CommonMark/GFM,原样显示 |
| Mermaid | 是 | 流程图、时序图、甘特图、饼图、思维导图,构建时转 SVG |
| 原始 HTML | 是 | 按 JSX 处理,<details>、<kbd> 都能用 |
这一页的写法大多是为了压测渲染器,日常写文章用不到这么多花样,真正常用的还是标题、段落、列表、代码块和表格。哪天换了插件,我会回到这里重新跑一遍,看哪一段又变了。
Footnotes
-
这是脚注定义,渲染时会排到文章末尾。 ↩
