内容集合(Content Collections)是 Astro 提供的一层包装:把一个目录下的 Markdown 文件当成带字段的数据记录,然后用 schema 校验每条记录的字段。
为什么需要它
Markdown 文件顶部的元数据用的是 YAML,天生没有类型。pubDate 拼成 pubdate,程序不会报错,只会安静地拿不到日期。文章还在,只是排到了错误的位置,或者干脆被过滤掉。
加一层 schema 之后,同一件事变成构建失败,报出文件名和字段名。错误从「用户发现页面不对」提前到「你敲下保存的那一秒」。
三个组成部分
| 部分 | 作用 |
|---|---|
loader | 去哪找文件,比如 glob({ base: './src/content/blog', pattern: '**/*.md' }) |
schema | 每个字段应该是什么类型,用 zod 描述 |
collections | 把上面两者导出,供页面里 getCollection() 使用 |
校验之外的两个好处
- 编辑器提示:字段有类型,敲
post.data.会自动补全所有可用字段名。 - 转换一次,处处省事:用
transform把"a/b/c"这种字符串统一转成数组,页面代码就不用到处判断格式。
页面里怎么用
---
import { getCollection } from 'astro:content';
const posts = await getCollection('blog');
---
{posts.map((post) => <a href={`/blog/${post.id}/`}>{post.data.title}</a>)}
post.id 是文件名(去掉扩展名),它决定了文章网址。所以改文件名等于改网址,已经发出去的链接会失效。
和其他方案的对比
- 纯 frontmatter:没有任何校验,靠人记。
- 外部数据库:内容在数据库里,写文章要开后台,本地的文本编辑器形同虚设。
- 内容集合:文件是纯文本,可以进 git,同时又有类型检查。
一个反直觉的点
集合里的文件不是「页面」,是「数据」。页面是从数据渲染出来的。所以你可以从同一批数据渲染出列表页、详情页、RSS、标签页、搜索索引,各写各的。