本站 Markdown 正文速查:哪些语法真生效,哪些只是看着像

这篇与《front-matter 全字段速查》《site.yml 全字段速查》是一套:那两篇管头部(字段怎么填),这篇管正文(写下去会变成什么)。文里每条结论都是把语法真的过一遍构建期渲染器得到的(探针 site/.shots/probe-md-matrix.mjs,可复跑)。

一、一条链:md 是怎么变成页面的

source/posts/**/*.md 到页面之间没有魔法,只有五步:

  1. gray-matter 把文件切成 front-matter 与正文两段——头部走 schema 校验(见另两篇速查),正文进渲染器;
  2. markdown-it固定三开关渲染:html:false · linkify:false · typographer:false
  3. 同一趟把标题清单抽出来(带 id 与层级),喂详情页右侧目录;
  4. 落盘 .content/posts.json——正文已经是 HTML 了;
  5. 页面层 PostBody 把这段 HTML 原样直出dangerouslySetInnerHTML)。

第 5 步是关键:浏览器里不跑 markdown 解析器,渲染结果在构建期就定死了。所以语法写错不会在浏览器里"兜回来"——要么构建期报错,要么原样出文本。

二、开着的:日常够用的那一套

写法 出什么 备注
## 标题 / ### 标题 h2 / h3,自动带 id 中文也行:## 二、小节id="二小节";id 重了自动加 -1;全部层级都进目录
**粗** *斜* ~~删除线~~ strong / em / s 三样都开
无序 / 有序 / 嵌套列表 ul / ol / 嵌套 标记是 ○ 与数字,颜色走淡墨
表格 table 1px 发丝线 + 6/12 内距(本文到处都是)
> 引用 blockquote 左侧 2px 强调色竖线,字色淡一档
行内 code code(浅色小片) 与围栏无关,走另一条样式
围栏代码(三个反引号) pre 深色块 语言名挂成 class="language-ts"——但本站没有高亮器,只挂名不染色
--- hr 分割线
[文字](/portfolio) a 站内相对地址与 http(s) 都行
![说明](/images/…) img 块级居中 + 圆角;说明 进 alt
<https://example.com> a 尖括号自动链接有效(它不归 linkify 管)
行尾两个空格 <br> 想硬换行就补两个空格,别指望单回车

代码块还有一项专属开关:front-matter 写 highlight_shrink: true,每个代码块会默认收成 20rem 高、右下角挂一枚胶囊按钮展开(按钮文字住 site.ymla11y.post_code_expand / post_code_collapse)。下面就是被折叠的样子:

// highlight_shrink: true 时这里默认是折起来的
export function resolvePost(fm: FrontMatter, cfg: PostDefaults) {
  return { toc: fm.toc ?? cfg.toc.enable }
}

三、关着的:写了也不按你想的来(逐条实测)

写法 实际结果
<div>…</div> 等原始 HTML 被转义成文本&lt;div&gt;)。html:false 是写死在 scripts/content/paths.ts 的安全不变量:正文是 dangerouslySetInnerHTML 直出,React 不做转义,放行 HTML 等于放行 XSS
<iframe …> 同样转义。要嵌播放器请走第四节的块级语法
裸 URL https://example.com 不成链接,原样文本(linkify:false
脚注 [^1] 会变成一个指向乱码地址的链接——[^1]: 内容 被当成引用式链接定义,正文里的 [^1] 就指了过去,href 是整段正文的 URL 编码。这是本站最容易踩的坑,别用
任务列表 - [ ] 待办 只出文本 [ ] 待办
定义列表(词条 + : 解释 一个普通段落
emoji 短代码 :smile: 原样文本
HTML 注释 <!-- … --> 转义成文本(不会消失,会当着读者面显示)

看到"被转义"别失望——真正需要第三方播放器的场景,第四节给了专门语法,而且它比放行 HTML 更安全:地址要过白名单。

四、本站特有的三件正文写法

1. 正文中间插播放器:块级 @[标签](播放器地址)

独立成行写这一行,播放器就落在正文的这个位置(不是正文前面,也不是顶部):

@[B 站 · 《重回1985》12 集全集连播](https://player.bilibili.com/player.html?bvid=BV1mfK767EqC&autoplay=0&high_quality=1)
  • 方括号=标签(作 iframe 的可访问名,必填);圆括号=播放器地址;
  • 三道闸与 front-matter 的 embeds 完全同口径:必须 https / 粘 B 站页面地址会被教着换成播放器地址 / host 必须命中白名单——构建期报错并指出文件,坏块不会交付;
  • 独立成行才生效(块级规则);写在句子中间不识别,会原样留在段落里;代码围栏里当然也不生效(上面那段就是围栏里演示的);
  • 渲染成与「视频」分节同一套 markup,零新增 CSS、零前端 JS,预渲染照旧带播放器;
  • 想让它占顶部槽(16/9 独占版心)是另一条路:front-matter 的 embeds + embed_hero: true

2. 【占位】 会被认出来

方括号里带「占位」的标记,本站不当作普通文字:

  • 出现在图片/视频槽位cover / top_img / video.src / video.poster):归一成"没有这一槽",页面退化成占位样式,不留死链
  • 出现在正文里:构建期包一层 <span class="ph">(虚线框小字),并计入上线检查表的「素材清零」进度。

也就是说:素材没到位时,写占位比留空更诚实——读者看到的是一处明确标记,而不是一个破图。

3. 素材路径:就近写母版,正文图片不必先被 front-matter 引用

正文里图片写交付地址 /images/<slug>/x.webp;母版放 source/images/<slug>/x.webp 就行。source/images/ 整个目录会被构建期搬进 dist/images/(与 source/video//media/video/ 同规),所以不必先在头部声明一次才能用。

五、排版不用你操心

正文的观感不由你写,由 .prose 一套口径给:段距 16px、列表用 ○ 标记(标记色淡墨)、h2 22px / h3 19px、表格 1px 发丝线、图片块级居中带圆角、引用左侧 2px 强调线、代码块深底圆角、链接取文章强调色main_color 可逐篇改)。

反过来说:别想在正文里写内联样式——原始 HTML 进不来(见第三节),而这类口径的唯一定义住 src/styles/,改样式是改那个地方,不是改某一篇的正文。

六、三个真会咬人的坑

  1. 标题自带编号 + toc_number: true = 双编号。本站的目录编号是 CSS 计数器做的(h2 出「1.」、h3 出「1.1」)。你既手写 ## 一、… 又开着 toc_number,正文和目录就会各出一套数字。构建期会提醒(认得出「一、」「1.」「1.1 」这几种写法),二选一:要么手写编号 + toc_number: false(这几篇速查文都是这样),要么不写编号、交给自动编号。
  2. 正文里的链接不过构建期校验。front-matter 的 linkssite.yml 的内链都会被查(路由 / 锚点 / 落盘),但正文里的链接不查——内链打错字,构建不会拦,线上就是 404。自己核。
  3. 正文图片也不查存在性。只认交付地址(/images/…);母版少了、文件名写错了,构建不报错,页面上是一个破图。发布前扫一眼正文里的 ![](/images 是划算的。

七、边界(如实说明)

  • 没有语法高亮:代码块只认语言名(挂在 class 上),不染色——本站没有引高亮器,将来接上也不用改正文;
  • 没有公式与音乐播放器的真渲染mathjax / katex / aplayer 目前只落成容器上的 data-* 钩子,第三方脚本未接入,写了不报错但也不会渲染;
  • 目录收全部标题层级(h1–h6,按层级嵌套):所以正文里别用 # 一级标题——页面标题已经是 h1,正文再出一个 h1 是抢层级,也会一起进目录;
  • 本篇的每条"实测"结论都出自 .shots/probe-md-matrix.mjs:改渲染器(比如将来放开某个开关)之后,跑一遍就知道口径有没有漂。