Frontmatter 和 Markdown 的关系(research digest)
一句话核心
Frontmatter 不是 Markdown。 它是 Jekyll 在 2008 年两小时里发明的 app 级约定,CommonMark 和 GFM 规范都明确不收它;之所以看起来”像是 Markdown 的一部分”,只是因为几乎每个现代 Markdown 工具链都在核心 parser 前面插了一层预解析剥离层来伪造支持。
研究背景
写 md 时文件头的 ---\nYAML\n--- 到底是什么?它是
Markdown 语法吗?为什么 Typora 能识别、vim 也能显示、但 CommonMark
官方文档里找不到它?本研究从起源 / 规范 / 格式家族 / 语义约定 /
工具链 / 边界六个维度梳理,回答”frontmatter 和 Markdown
到底是什么关系”。
关键发现(Top 8)
起源精确到分钟:2008-10-20 02:07 UTC,Tom Preston-Werner 创建
autoblog(后改名 Jekyll);两小时后 commitecda2748(“parse out yaml from posts”)用一行正则/^(---\n.*?)\n---\n/完成了 frontmatter 的首次实现。这就是---\n...\n---约定的历史源头。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)。
Pandoc 是独立发明:Pandoc 的
pandoc_title_block(% title / % author)早于 Jekyll,和 YAML 无关;Pandoc 的yaml_metadata_block2013-07-02 才加入(commit3cd62d7c),比 Jekyll 晚近 5 年,是跟着 Jekyll 既成事实走的。三横线困境:在 vanilla CommonMark 里,
---同时是(a)YAML frontmatter 分隔符、(b)thematic break、(c)setext H2 下划线,且优先级是 c > b。没有 frontmatter 扩展的 parser 遇到---\ntitle: Hello\n---会把前两行解析成一个叫 “title: Hello” 的 H2 标题——这是 CommonMark 不敢轻易 standardize frontmatter 的技术原因。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)得势。共识核只有 5 个字段:
title/description/tags/slug/date,其中date歧义最严重——Jekyll 的published: false是布尔,Hugo 的published: 2026-01-01是字符串日期,同名不同语义,跨工具迁移时最容易踩的坑。其他字段几乎都是工具特定 DSL。工具链的两层结构:每个 Markdown pipeline 都要做两件事——(A)语法层识别并剥离
---块,(B)数据层把 YAML 字符串解析成对象。remark 生态把这两层显式拆成两个插件(remark-frontmatterREADME 原话:“Doesn’t parse the data inside them: create your own plugin to do that.”);gray-matter 和 Pandoc 选择一体化。markdown-it 官方甚至没有 frontmatter 支持,由社区插件补齐。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 派的 +++
正是这种选择)。但给定历史惯性,今天没人会为优雅重新发明它。
三条可迁移洞察
两层架构是所有”结构化文本头”的通用解:Markdown frontmatter / shebang 行 / Ruby magic comment / Python PEP 263 / LaTeX
%!TEX/ Jupyter cell magic / HTTP header—— 所有想在纯文本里塞结构化数据的场景都遵循”先识别、后剥离、独立 pass”的模式。remark 把 recognizer 和 parser 显式拆成两个插件是教科书级示范。单文件原子性 vs sidecar 的权衡是普遍的:元数据少就 inline,元数据多就 sidecar。平衡点随”内容占比 vs 元数据占比”变化。同类选择散落在 Jupyter / Docker LABEL / Git commit trailers / Python pyproject.toml 等地方。
生态惯性决定的”标准”不必优雅:frontmatter 是”一个两小时写的 app 约定被生态选中,变成 15 年不能撼动的事实标准”的典型案例。评估”粗糙但已经赢了的约定”时,要看替换成本,而不是只看缺陷成本——JSON 无注释、CSV 无规范都是同类例子。
回到原问题
Markdown 负责渲染一段文本到 HTML。Frontmatter 负责告诉工具链”这段文本是什么、什么时候发布、放在哪里、归档给谁”。两者是并列关系,不是包含关系。Markdown 核心 parser 对 frontmatter 一无所知,是每个工具自己往管道前端加的预处理层让它们看起来像一体。
三层关系:
- 规范层:frontmatter 不是 Markdown(CommonMark / GFM 都不收)
- 生态层:frontmatter 事实上是 Markdown(几乎所有工具都支持)
- 语义层:frontmatter 比 Markdown 更混乱(工具私有
DSL,跨工具迁移时只有
title/tags/description/slug能裸搬)
实用建议
- 跨 SSG 场景:用 Hugo 的保留字表(约 30
个)作为字段命名基线(它最严格),自定义字段统一放进
params子键 - 纯 Obsidian 场景:注意 Obsidian 会自动 re-format YAML,别依赖手写格式;同名字段在 vault 里会被强制同类型
完整 research 报告见
Learning/Research/2026-04-11-frontmatter-and-markdown.md(34
个一手 source)。