← 全部文章

Writing

把 Markdown 当数据用:内容集合的一点心得

约 2 分钟Astro内容管理工程化

用 Markdown 管内容是件很舒服的事:纯文本、能进 Git、随手就能改。但它有个不太明显的问题——frontmatter 里的字段是没人管的

问题长什么样

假设每个作品文件都要写标题、标签、作品地址。半年后你新增一个文件,字段名写成了 taglines(多了个 s),或者忘了写 demo

编辑器不会报错,Git 也不会。它会一路通过构建,直到线上某个页面上那块内容是空的——或者更糟,构建时抛出一个看不懂的 undefined 错误,而你得花时间去找是哪个文件。

Markdown 的灵活性,在这里正好变成了它的弱点。

schema 把它变成构建期错误

内容集合的核心价值,是把这些字段声明成有约束的数据结构

const works = defineCollection({
  loader: glob({ pattern: '**/*.md', base: './src/content/works' }),
  schema: z.object({
    title: z.string(),
    tagline: z.string(),
    status: z.enum(['live', 'wip', 'archived']).default('live'),
    demo: z.object({
      embed: z.string().optional(),
      interactive: z.boolean().default(false),
    }).default({}),
  }),
});

带来的变化很直接:

  • 字段名写错 → 构建直接失败,不用等上线
  • 忘了写必填字段 → 同上
  • status 写成了 published → 立刻报错,因为只允许三个枚举值
  • 可选字段没写 → 自动拿默认值,模板里不用到处判空

把运行时的问题提前到构建期,这是它最大的价值。构建失败会立刻被你看到,线上空白不会。

一个反直觉的点

加 schema 会让“新增一个作品”变慢吗?

恰恰相反。没有 schema 时,你需要记住每个字段叫什么、哪些必填、格式是什么——这些约束只存在于你脑子里,或者散落在某个文档里。

有了 schema,约束写在代码里,编辑器会直接提示。新增文件时照着补全就行,比翻文档快。

顺带解决的问题:类型

定义 schema 之后,读取内容的代码会自动获得类型推导。getCollection('works') 拿到的每一项,data.titlestringdata.status 是那三个字面量之一。

这意味着模板里写 data.demo.interactive 会有补全和类型检查,而不是盲猜。

对一个会长期维护的个人站来说,这些约束加起来的价值,远超过定义 schema 所花的那点时间。