跳到主要内容
万两金的个人博客

Astro 内容集合使用笔记

约 2 分钟阅读

内容集合(Content Collections)是 Astro 里用来管理结构化内容的机制。这次搭站把踩到的点记一下。

目录与入口配置

内容放在 src/content/<集合名>/ 下,集合本身在 src/content.config.ts 里声明。

import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';
import { z } from 'astro/zod';

const blog = defineCollection({
  loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
  schema: z.object({
    title: z.string().max(60),
    pubDate: z.coerce.date(),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
  }),
});

export const collections = { blog };

两个容易踩的细节

一是 z 的导入路径。astro:content 导入 z 目前仍然可用,但已经被标记为废弃,官方建议改用 astro/zod。新项目直接按后者写,省得以后迁移。

二是 z.coerce.date() 的必要性。 frontmatter 里的日期是字符串,不做 coerce 拿到的就是字符串,getTime() 之类的调用会在运行时才报错。

查询与排序

const posts = await getCollection('blog', ({ data }) => !data.draft);
posts.sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime());

过滤条件写在 getCollection 的第二个参数里,比取出来再 filter 更直观。注意排序要显式做,集合的返回顺序不保证。

草稿的处理

import.meta.env.PROD 区分环境,生产构建排除草稿,本地开发保留以便预览:

const posts = await getCollection('blog', ({ data }) =>
  import.meta.env.PROD ? !data.draft : true
);

渲染方式的变化

新版把实例方法改成了独立函数,从 entry.render() 变成 render(entry)

import { render } from 'astro:content';

const { Content, headings } = await render(entry);

返回的 headings 可以直接用来生成文章目录,不用自己解析 Markdown。

小结

内容集合的价值主要在两个地方:一是 frontmatter 有类型校验,字段写错在构建期就能发现;二是内容与页面彻底解耦,新增文章只需要丢一个 Markdown 文件进目录。