Astro Content Collections 入门

Astro Content Collections 半小时上手:schema、类型安全、frontmatter,让 500 篇规模的站点也可维护的工作流。

Content Collections 把 Markdown 文件夹变成有类型的数据库。半小时学习,每次加字段或改 slug 都回本。

问题背景

没有 Content Collections,Astro 把每个 Markdown 当散文本。有了之后,你得到 schema 校验、自动补全、统一的查询 API。30 篇以上的站,这就是”心里有底”和”一团乱”的区别。

判断标准

  • 已经或预期超过 30 篇 Markdown / MDX 文件。
  • frontmatter 写错时希望编译期就报错。
  • 多个模板要读同一份内容(首页、hub 页、sitemap、RSS)。
  • 站点要维护数年而不是数周。

快速结论

2026 年用 Astro 起内容站,第一天就上 Content Collections。后期补,比一开始就学要贵。

实操步骤

  1. src/content/articles/ 目录,放几个带 frontmatter 的 MDX 文件:
---
title: "Hello world"
description: "一篇测试文章,验证 content collections 是否工作。"
publishedAt: 2026-05-22
tags: ["intro", "test"]
author: "alice"
---

# Hello

这是正文。
  1. src/content/config.ts 里写 schema。Schema 是契约——任何文件不满足就编译失败:
import { defineCollection, reference, z } from 'astro:content';

const articles = defineCollection({
  type: 'content',
  schema: ({ image }) => z.object({
    title: z.string().min(10).max(80),
    description: z.string().min(120).max(160),
    publishedAt: z.date(),
    updatedAt: z.date().optional(),
    tags: z.array(z.string()).min(1),
    cover: image().optional(),
    author: reference('authors'),
    draft: z.boolean().default(false),
    featured: z.boolean().default(false),
  }),
});

const authors = defineCollection({
  type: 'data',                       // JSON / YAML,不是 markdown
  schema: z.object({
    name: z.string(),
    bio: z.string(),
    twitter: z.string().optional(),
  }),
});

export const collections = { articles, authors };
  1. npm run dev,Astro 会逐文件校验、明确指出错在哪——红字全清掉再继续:
npm run dev
# Could not parse content collection 'articles':
# src/content/articles/old-post.mdx
#   description: String must contain at most 160 character(s)
  1. src/content/authors/ 加数据文件:
# src/content/authors/alice.yaml
name: Alice Chen
bio: 独立开发者,写内容站相关。
twitter: alicewrites
  1. 渲染列表页——结果是类型化的,编辑器会补全 entry.data.title
---
// src/pages/blog/index.astro
import { getCollection } from 'astro:content';

const posts = (await getCollection('articles', e => !e.data.draft))
  .sort((a, b) => +b.data.publishedAt - +a.data.publishedAt);
---
<ul>
  {posts.map(p => (
    <li>
      <a href={`/blog/${p.slug}/`}>{p.data.title}</a>
      <time datetime={p.data.publishedAt.toISOString()}>
        {p.data.publishedAt.toLocaleDateString()}
      </time>
    </li>
  ))}
</ul>
  1. 单篇路由放 src/pages/blog/[...slug].astro,并把 author reference 解析出来:
---
import { getCollection, getEntry } from 'astro:content';

export async function getStaticPaths() {
  const posts = await getCollection('articles', e => !e.data.draft);
  return posts.map(p => ({ params: { slug: p.slug }, props: { entry: p } }));
}

const { entry } = Astro.props;
const author = await getEntry(entry.data.author);
const { Content } = await entry.render();
---
<article>
  <h1>{entry.data.title}</h1>
  <p>作者:{author.data.name}</p>
  <Content />
</article>
  1. 生产检查——build 一次确认没有 schema 漏网:
npm run build
# Generating static routes
#  ✓ generated 247 routes

容易踩的坑

  • 跳过 schema 用裸 Markdown——Content Collections 的全部优势都丢了。
  • 为了”过编译”把必填字段写成可选,让脏数据混进来。
  • 把图片路径存成字符串而不是用 image() schema helper,导致优化失效。
  • 改完 schema 没重启 dev server,老类型还在。
  • 把 Content Collections 当成给非技术编辑用的 CMS——他们还是要写 Markdown。

这篇适合谁

做内容多、需要类型安全和可靠性的 Astro 开发者。

这篇不适合谁

10 页以内的小站,schema 成本回不来。

FAQ

  • 支持 MDX 吗: 原生支持 .md.mdx。MDX 文件里 frontmatter 和组件可以共存。
  • 能放非文本内容比如 JSON 吗: 可以,用 type: "data" 的 collection 放 JSON 或 YAML。适合存作者、标签、配置。
  • frontmatter 写错会怎样: 构建直接失败,错误信息明确指向哪个文件哪个字段。这是 feature,不是 bug。
  • 怎么迁移已有 Markdown 目录: 把文件挪到 src/content/<name>/,写 schema,跑 npm run dev,逐个改到全过再发布。

相关阅读

标签: #独立开发 #Astro #Content Collections #MDX #入门