返回文章

开发实践

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,维护成本会更低。