Frontmatter 用法与规范
详细说明 iflux-art 项目中 blog 和 docs 两个站点的 Frontmatter 字段定义、文件组织方式和侧边栏配置,包括 _meta.json 排序机制和 Zod 校验逻辑。
iflux-art 项目包含两个内容站点:blog(技术博客)和 docs(文档中心)。它们共享同一套内容引擎 @iflux-art/content-engine,但在 Frontmatter 字段、文件组织和侧边栏配置上各有差异。
这篇文章系统梳理两者的规范,方便后续写作时直接参考。
一、Frontmatter 基础
Frontmatter 是 Markdown/MDX 文件顶部的 YAML 元数据区域,用 --- 包裹,用于定义文章的标题、日期、分类等信息。
---
title: 文章标题
excerpt: 文章摘要
category: 分类名
tags: [标签1, 标签2]
date: "2026-04-26"
---
两个站点的内容目录位于各自的 src/content/ 下,支持 .md 和 .mdx 两种格式。构建时通过 gray-matter 解析 Frontmatter,并通过 @iflux-art/content-engine 的 Zod Schema 进行校验。
二、Blog 站点
文件组织
Blog 按分类目录组织文件,目录名即为分类名:
src/content/ ├── 开发/ │ ├── obsidian-r2-uploader.md │ └── MDX 自定义组件库.md ├── 羊毛/ │ ├── 内测链接与邀请码汇总.md │ └── 大模型调用 API 免费额度汇总.md └── 模版/ └── blog 模版.md
文件的 URL slug 由 目录名 + 文件名(去掉扩展名)组成,例如 开发/obsidian-r2-uploader.md 的 slug 是 开发/obsidian-r2-uploader,访问路径为 /posts/开发/obsidian-r2-uploader。
以点开头的目录(如
.obsidian)会被自动跳过,不会被纳入构建。
Frontmatter 字段
Blog 的 Frontmatter 通过 ContentFrontmatterSchema 进行 Zod 校验:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
title | string | 是 | 文章标题,不能为空 |
date | string | 是 | 发布日期,格式 YYYY-MM-DD |
excerpt | string | 否 | 文章摘要,显示在文章列表卡片 |
category | string | 否 | 分类名称。不填时自动取目录名 |
tags | string[] | 否 | 标签数组 |
cover | string | 否 | 封面图片 URL |
author | string | 否 | 作者名 |
draft | boolean | 否 | 是否为草稿,true 时不会出现在列表中 |
featured | boolean | 否 | 是否为精选文章,首页会展示精选卡片 |
字段详解
title
文章的唯一标识之一,会显示在文章详情页、列表卡片和浏览器标签栏中。同时作为 SEO 的 <title> 和 Open Graph 标题。
date
控制文章在列表中的排序(新文章在前),也用于 sitemap 的 lastModified 和 Open Graph 的 publishedTime。推荐加引号以避免 YAML 将日期解析为对象:
date: "2026-04-26"
category
分类有两种来源,优先级从高到低:
- Frontmatter 中的
category字段 — 显式指定 - 文件所在目录名 — 自动推断
例如 开发/obsidian-r2-uploader.md 不写 category 时,自动归类到 开发。
分类支持嵌套目录,如 技术/前端/react-hooks.md 的分类是 技术/前端。
tags
标签用于文章筛选,侧边栏会展示所有标签及其文章数量。两种写法等价:
tags: [标签1, 标签2, 标签3]
tags:
- 标签1
- 标签2
- 标签3
draft
草稿标记,设为 true 时文章不会出现在列表页和 API 返回中,但直接访问 URL 仍然可以看到:
draft: true
featured
精选标记,首页会优先展示精选文章(最多 4 篇):
featured: true
完整示例
---
title: WSL2 安装 Arch Linux 完整指南
excerpt: 在 WSL2 中安装 Arch Linux 的完整步骤,包括系统初始化、镜像源配置和开发环境搭建。
category: 开发
tags: [Arch Linux, WSL2, Linux安装]
date: "2026-04-22"
cover: https://img.iflux.art/arch-wsl2-cover.png
featured: true
---
模版文件
Blog 提供了模版文件 模版/blog 模版.md,新建文章时可以直接复制:
---
title: 标题
excerpt: 描述
category: 模版
tags:
- 标签1
- 标签2
- 标签3
date: 2026-04-10
---
三、Docs 站点
文件组织
Docs 同样按目录组织,但额外引入了 _meta.json 文件来控制侧边栏的排序和显示名称:
src/content/ ├── _meta.json ├── index.mdx ├── grammar/ │ ├── _meta.json │ ├── markdown.md │ ├── json.md │ └── ... ├── system/ │ ├── _meta.json │ ├── linux.md │ └── ... ├── agent/ │ ├── _meta.json │ ├── claude-code/ │ │ └── _meta.json │ │ └── wsl2-cc.md │ └── hermes-agent/ │ ├── _meta.json │ ├── wsl2-hermes.md │ └── hermes-feishu.md └── local/ ├── _meta.json └── docker.md
_meta.json 机制
_meta.json 是 Docs 站点独有的侧边栏配置文件,每个目录下都可以放置一个。它的作用有两个:
- 定义显示名称 — 将目录名或文件名映射为更友好的中文名称
- 控制排序 — 按照配置顺序渲染侧边栏
格式
每个 _meta.json 是一个 JSON 对象,键为文件名或目录名,值为显示名称(字符串):
{
"grammar": "语法基础",
"system": "操作系统",
"agent": "智能体",
"local": "本地部署"
}
多级嵌套
子目录可以有自己的 _meta.json,控制子项的排序和名称:
{
"hermes-agent": "Hermes Agent",
"openclaw": "OpenClaw",
"claude-code": "Claude Code"
}
再下一级同样支持:
{
"wsl2-cc": "WSL 2 上安装 Claude Code 的完整指南"
}
标题优先级
文档标题的来源优先级(从高到低):
_meta.json中配置的名称 — 最高优先- Frontmatter 中的
title字段 - 文件名(去掉扩展名)作为降级
未在 _meta.json 中配置的项
如果某个目录或文件没有在 _meta.json 中列出,它仍然会出现在侧边栏中,只是排在已配置项的后面,并使用文件名或 Frontmatter 标题。
Frontmatter 字段
Docs 的 Frontmatter 比 Blog 简洁,定义在 DocFrontmatter 接口中:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
title | string | 否 | 文档标题(会被 _meta.json 覆盖) |
excerpt | string | 否 | 文档摘要 |
order | number | 否 | 排序权重 |
display | "hidden" | 否 | 设为 hidden 时在侧边栏中隐藏 |
Docs 不需要
date、tags、category等字段,因为分类由目录结构决定,排序由_meta.json控制。
display: hidden
在 _meta.json 中配置或在 Frontmatter 中设置,用于隐藏特定文档:
---
display: hidden
---
完整示例
---
title: WSL2 安装与通用配置指南
excerpt: Windows 上的 WSL2 从零安装到开发环境搭建的完整配置流程。
order: 2
---
模版文件
Docs 的模版在 模版/docs 模版.md:
---
title: 标题
excerpt: 描述
category: 模版
tags:
- 标签1
- 标签2
- 标签3
date: 2026-04-10
---
四、两者对比
| 特性 | Blog | Docs |
|---|---|---|
| 内容目录 | apps/blog/src/content/ | apps/docs/src/content/ |
| 文件格式 | .md / .mdx | .md / .mdx |
| 分类方式 | 目录名 = 分类名 | 目录名 = 分类名 |
| 侧边栏排序 | 按文章日期倒序 | _meta.json 配置 |
| 显示名称 | 取 frontmatter title | _meta.json 优先 |
| 日期字段 | date(必填) | 无需 |
| 标签系统 | 有(侧边栏标签云) | 无 |
| 草稿机制 | draft: true | display: hidden |
| 精选文章 | featured: true | 无 |
| 封面图 | cover 字段 | 无 |
| Frontmatter 校验 | Zod Schema(必填 title、date) | 无严格校验 |
Blog 以时间线组织内容,侧重发布日期和标签分类;Docs 以目录树组织内容,侧重层级结构和 _meta.json 排序。两者都支持通过目录嵌套实现多级分类。
五、写作流程
Blog 新建文章
- 在
src/content/下选择合适的分类目录(或新建目录) - 创建
.md文件,文件名用英文短横线格式 - 复制模版填写 Frontmatter
---
title: 文章标题
excerpt: 一句话描述文章内容
category: 开发
tags: [标签1, 标签2]
date: "2026-04-26"
---
- 正文使用 Markdown + MDX 自定义组件编写
Docs 新建文档
- 在
src/content/对应分类目录下创建.md文件 - 在该目录的
_meta.json中添加条目并设置显示名称 - 编写 Frontmatter(只需
title和excerpt)
---
title: 文档标题
excerpt: 文档简要描述
---
- 正文同样使用 Markdown + MDX 自定义组件
六、Zod 校验规则
Blog 的 Frontmatter 通过 @iflux-art/content-engine 提供的 safeParseFrontmatter 函数进行校验,底层使用 Zod:
const ContentFrontmatterSchema = z.object({
title: z.string().min(1, "标题不能为空"),
date: z.string().min(1, "日期不能为空"),
excerpt: z.string().optional(),
cover: z.string().optional(),
author: z.string().optional(),
tags: z.array(z.string()).optional(),
category: z.string().optional(),
draft: z.boolean().optional(),
featured: z.boolean().optional(),
});
校验失败时不会导致构建中断,而是降级使用默认值(标题取文件名,日期取当天),并在日志中输出警告。这个设计保证了即使 Frontmatter 写得不太规范,文章依然可以正常渲染。
虽然校验失败有降级处理,但建议始终填写 title 和 date 两个必填字段,避免文章以文件名和当天日期出现在列表中。