Astro 内容集合实战
用 Astro 7 的内容集合统一管理 Markdown 元数据,并生成类型清晰的文章列表与详情页。

Markdown 很适合维护文章,但文件一多,标题、日期和封面路径很容易出现缺失或格式不一致。Astro 的内容集合可以在构建阶段校验这些元数据,并为查询结果提供类型提示。
下面用一个最小的文章集合说明完整做法。
建立内容目录
把文章放在统一目录中,每篇文件使用 frontmatter 描述页面需要的信息:
src/
├── content.config.ts
└── content/
└── articles/
├── first-post.md
└── second-post.md
文章文件可以这样开始:
---
title: "第一篇文章"
description: "这是一段用于列表和搜索摘要的说明。"
date: 2026-08-11
category: "开发实践"
cover: "/images/example.png"
---
放在 public 目录中的图片可以直接使用以 / 开头的地址。若需要 Astro 处理和优化图片,则可以改用 src 内的图片资源,并在 schema 中使用图片辅助类型。
定义集合和字段规则
Astro 7 可以使用 glob loader 读取本地 Markdown 与 MDX:
import { defineCollection, z } from "astro:content";
import { glob } from "astro/loaders";
const articles = defineCollection({
loader: glob({
base: "./src/content/articles",
pattern: "**/*.{md,mdx}",
}),
schema: z.object({
title: z.string(),
description: z.string(),
date: z.coerce.date(),
category: z.string(),
cover: z.string(),
}),
});
export const collections = { articles };
这里用 z.coerce.date() 把 frontmatter 中的日期转换为 Date。字段漏填或类型不正确时,构建会直接报错,问题不会拖到页面渲染阶段才出现。
查询并排序文章
页面中通过 getCollection 获取集合。文章列表通常还需要按日期倒序排列:
---
import { getCollection } from "astro:content";
const articles = (await getCollection("articles")).sort(
(a, b) => b.data.date.valueOf() - a.data.date.valueOf(),
);
---
<ul>
{articles.map((article) => (
<li>
<a href={`/articles/${article.id}/`}>{article.data.title}</a>
</li>
))}
</ul>
集合条目中的 data 对应 schema 校验后的元数据,id 则可以用来生成详情页地址。
生成文章详情页
静态详情页可以通过 getStaticPaths 在构建时生成参数,并把集合条目传给页面:
---
import { getCollection, render } from "astro:content";
export async function getStaticPaths() {
const articles = await getCollection("articles");
return articles.map((article) => ({
params: { slug: article.id },
props: { article },
}));
}
const { article } = Astro.props;
const { Content } = await render(article);
---
<article>
<h1>{article.data.title}</h1>
<Content />
</article>
这套结构的核心价值不是文件组织本身,而是给内容建立清楚的契约。列表页、详情页、站点地图和订阅源都读取同一份经过校验的数据,后续增加字段时也能在构建阶段发现遗漏。
对于规模不大的个人站点,先保留少量必需字段就足够了。只有当筛选、作者或多语言等需求真正出现时,再扩展 schema,维护成本会更低。