用 Astro 内容集合管理博客文章

从 schema 校验、目录约定到构建期过滤,记录内容集合在个人博客里的实际用法与取舍。

浅色背景上的文章标题与分隔线,展示博客封面排版
本文目录

内容集合(Content Collections)解决的问题很具体:把散落在目录里的 Markdown 变成有类型、可校验、可查询的数据。对个人博客来说,它的价值不在“功能多”,而在“坏数据会在构建期失败”。

为什么需要内容集合

没有 schema 的时候,frontmatter 只是一段 YAML。字段拼错、日期写成中文、标签随意新增,都要等到页面上出现空白时才被发现。内容集合把这些检查提前到构建阶段,并给出具体字段名。

目录与图片

文章以目录为单位,图片与正文放在一起。这样迁移时不需要额外维护一份资源映射,相对路径也不会因为文章换位置而失效。

Markdown 经 schema 校验后生成静态页面与索引的流程示意

schema 示例

下面是一段刻意加长的代码,用来检查代码块的横向滚动与复制按钮:

export const blog = defineCollection({
  loader: glob({ base: './src/content/blog', pattern: '**/index.{md,mdx}' }),
  schema: ({ image }) => z.object({
    title: z.string().min(1).max(80),
    description: z.string().min(20).max(160),
    slug: z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/),
    pubDate: z.coerce.date(),
    tags: z.array(z.enum(tagNames)).max(3).default([]),
    draft: z.boolean().default(false),
    cover: image().optional(),
  }),
});

字段对照

字段必填类型默认值校验规则用途示例备注
title是string—1–80 字符标题与 SEO你好,世界中文建议不超过 40 字
description是string—20–160 字符摘要与 meta一篇示例文章必须能独立成立
slug是string—kebab-caseURLhello-world全局唯一
pubDate是date—可解析日期排序与 RSS2026-10-18按站点时区过滤
updatedDate否date—≥ pubDate更新时间2026-10-20不替换发布日期
tags否string[][]注册表内分类前端最多 3 个
draft否booleanfalse—草稿过滤true构建时排除
cover否image—本地图片封面/uploads/astro-content-collections-cover.png需同时提供 coverAlt

构建期过滤

草稿与未来日期在构建时被排除,因此它们不会进入首页、列表、RSS 或搜索索引。日期按 Asia/Shanghai 的日历日比较,避免 YYYY-MM-DD 被解析成 UTC 午夜后,在当天早上被误判为未来。1

小结

内容集合不是银弹,但它把“内容约定”变成了可执行代码。对一个人维护的博客来说,这比多一套后台更有用。

Footnotes

  1. 具体实现见 src/lib/posts.ts 中的 isPublished()。 ↩

讨论

评论由 GitHub Discussions 提供,点击后加载。未加载前不会产生第三方请求。