← All Articles

Frontmatter 和 Markdown 的关系(research digest)

Learning/Research/2026-04-11-frontmatter-and-markdown.md · 2026-04-11

一句话核心

Frontmatter 不是 Markdown。 它是 Jekyll 在 2008 年两小时里发明的 app 级约定,CommonMark 和 GFM 规范都明确不收它;之所以看起来”像是 Markdown 的一部分”,只是因为几乎每个现代 Markdown 工具链都在核心 parser 前面插了一层预解析剥离层来伪造支持。

研究背景

写 md 时文件头的 ---\nYAML\n--- 到底是什么?它是 Markdown 语法吗?为什么 Typora 能识别、vim 也能显示、但 CommonMark 官方文档里找不到它?本研究从起源 / 规范 / 格式家族 / 语义约定 / 工具链 / 边界六个维度梳理,回答”frontmatter 和 Markdown 到底是什么关系”。

关键发现(Top 8)

  1. 起源精确到分钟:2008-10-20 02:07 UTC,Tom Preston-Werner 创建 autoblog(后改名 Jekyll);两小时后 commit ecda2748(“parse out yaml from posts”)用一行正则 /^(---\n.*?)\n---\n/ 完成了 frontmatter 的首次实现。这就是 ---\n...\n--- 约定的历史源头。

  2. CommonMark 明确不收:spec v0.31.2 零次提及 frontmatter / metadata。John MacFarlane 2014-11-20 在 talk.commonmark.org 只提议过”定义一个 recognizer 让 parser 知道跳过它”,连这个最弱版本都没被采纳。GFM Spec 同样零次出现 frontmatter / metadata / YAML——GitHub 能渲染它纯粹是应用层行为(Jekyll-based Pages、Docs、Gist)。

  3. Pandoc 是独立发明:Pandoc 的 pandoc_title_block% title / % author早于 Jekyll,和 YAML 无关;Pandoc 的 yaml_metadata_block 2013-07-02 才加入(commit 3cd62d7c),比 Jekyll 晚近 5 年,是跟着 Jekyll 既成事实走的。

  4. 三横线困境:在 vanilla CommonMark 里,--- 同时是(a)YAML frontmatter 分隔符、(b)thematic break、(c)setext H2 下划线,且优先级是 c > b。没有 frontmatter 扩展的 parser 遇到 ---\ntitle: Hello\n--- 会把前两行解析成一个叫 “title: Hello” 的 H2 标题——这是 CommonMark 不敢轻易 standardize frontmatter 的技术原因。

  5. YAML 一家独大:11 个主流工具(Jekyll/Hugo/Astro/MDX/Zola/VitePress/Docusaurus/11ty/Gatsby/Obsidian/Next.js MDX)里 10 个支持 YAML,8 个只支持 YAML。Hugo 是唯一三格式并列(YAML=---、TOML=+++、JSON=裸花括号),通过分隔符本身识别格式——这是有意思的设计选择。TOML 只在 Go 生态(Hugo/Zola)得势。

  6. 共识核只有 5 个字段title / description / tags / slug / date,其中 date 歧义最严重——Jekyll 的 published: false 是布尔,Hugo 的 published: 2026-01-01 是字符串日期,同名不同语义,跨工具迁移时最容易踩的坑。其他字段几乎都是工具特定 DSL。

  7. 工具链的两层结构:每个 Markdown pipeline 都要做两件事——(A)语法层识别并剥离 --- 块,(B)数据层把 YAML 字符串解析成对象。remark 生态把这两层显式拆成两个插件remark-frontmatter README 原话:“Doesn’t parse the data inside them: create your own plugin to do that.”);gray-matter 和 Pandoc 选择一体化。markdown-it 官方甚至没有 frontmatter 支持,由社区插件补齐。

  8. Obsidian Properties 改的是 UI 不是存储:2023-08 Obsidian 1.4 把 frontmatter 改称 “Properties”,底层仍是磁盘上的 YAML 文本;但它加了 vault 级全局类型约束,显式不支持 nested / markdown-in-properties——所以 Obsidian 的 Properties 已经是 YAML 的受限子集,“frontmatter = 纯 YAML” 的等式在 Obsidian 语境下不再严格成立。

Steelman:frontmatter 是设计缺陷还是工程妥协?

正方(缺陷论):文件头不是 Markdown;--- 三重语义真实冲突;YAML 有 sharp edges(Norway problem、datetime 歧义);每个工具链都必须多写一个预解析层才能让核心 parser 正常工作;更优雅的设计应该用 HTML <meta> 或 sidecar JSON。

反方(妥协论):单文件原子性是内容管理的巨大胜利——一个 .md 同时承载内容 + 元数据 + 版本控制,无 sidecar drift;frontmatter 是 render-target-agnostic 的,同一个 YAML 可以喂给 HTML / RSS / sitemap / LaTeX / 搜索索引,HTML <meta> 做不到(它是 render 的产物而非输入);15 年的生态惯性是网络效应而非债。

中间立场:设计上确实是 hack,工程上是正确妥协。如果能重来,要么做成 recognizer(jgm 2014 提案),要么换个不冲突的分隔符(TOML 派的 +++ 正是这种选择)。但给定历史惯性,今天没人会为优雅重新发明它。

三条可迁移洞察

  1. 两层架构是所有”结构化文本头”的通用解:Markdown frontmatter / shebang 行 / Ruby magic comment / Python PEP 263 / LaTeX %!TEX / Jupyter cell magic / HTTP header—— 所有想在纯文本里塞结构化数据的场景都遵循”先识别、后剥离、独立 pass”的模式。remark 把 recognizer 和 parser 显式拆成两个插件是教科书级示范。

  2. 单文件原子性 vs sidecar 的权衡是普遍的:元数据少就 inline,元数据多就 sidecar。平衡点随”内容占比 vs 元数据占比”变化。同类选择散落在 Jupyter / Docker LABEL / Git commit trailers / Python pyproject.toml 等地方。

  3. 生态惯性决定的”标准”不必优雅:frontmatter 是”一个两小时写的 app 约定被生态选中,变成 15 年不能撼动的事实标准”的典型案例。评估”粗糙但已经赢了的约定”时,要看替换成本,而不是只看缺陷成本——JSON 无注释、CSV 无规范都是同类例子。

回到原问题

Markdown 负责渲染一段文本到 HTML。Frontmatter 负责告诉工具链”这段文本是什么、什么时候发布、放在哪里、归档给谁”。两者是并列关系,不是包含关系。Markdown 核心 parser 对 frontmatter 一无所知,是每个工具自己往管道前端加的预处理层让它们看起来像一体。

三层关系

实用建议


完整 research 报告见 Learning/Research/2026-04-11-frontmatter-and-markdown.md(34 个一手 source)。