Astro 的内容集合(Content Collections)是一个强大的功能,用于管理结构化内容,如博客文章、文档、产品目录等。它提供了类型安全、性能优化和开发体验提升。
核心概念:
内容集合允许你在 src/content 目录下组织内容,并通过类型安全的 API 访问这些内容。
设置内容集合:
-
创建集合配置:
typescript// src/content/config.ts import { defineCollection, z } from 'astro:content'; const blog = defineCollection({ type: 'content', // 使用 Markdown/MDX schema: z.object({ title: z.string(), description: z.string(), publishDate: z.coerce.date(), tags: z.array(z.string()), image: z.string().optional(), }), }); const products = defineCollection({ type: 'data', // 使用 JSON/YAML schema: z.object({ name: z.string(), price: z.number(), category: z.string(), }), }); export const collections = { blog, products }; -
创建内容文件:
markdown<!-- src/content/blog/my-first-post.md --> --- title: "我的第一篇文章" description: "这是文章描述" publishDate: 2024-01-15 tags: ["astro", "tutorial"] image: "/images/post-1.jpg" --- 这是文章的正文内容...json// src/content/products/product-1.json { "name": "产品 1", "price": 99.99, "category": "电子" }
查询内容集合:
astro--- import { getCollection } from 'astro:content'; // 获取所有博客文章 const allPosts = await getCollection('blog'); // 按日期排序 const posts = allPosts .filter(post => post.data.publishDate <= new Date()) .sort((a, b) => b.data.publishDate.valueOf() - a.data.publishDate.valueOf()); // 获取特定标签的文章 const astroPosts = await getCollection('blog', ({ data }) => { return data.tags.includes('astro'); }); --- <h1>博客文章</h1> {posts.map(post => ( <article> <h2>{post.data.title}</h2> <p>{post.data.description}</p> <time>{post.data.publishDate.toLocaleDateString()}</time> <a href={`/blog/${post.slug}`}>阅读更多</a> </article> ))}
获取单个条目:
astro--- import { getEntry } from 'astro:content'; import type { CollectionEntry } from 'astro:content'; // 通过 slug 获取单个条目 const post = await getEntry('blog', 'my-first-post'); // 或者从 params 获取 const { slug } = Astro.params; const post = await getEntry('blog', slug); if (!post) { return Astro.redirect('/404'); } const { Content } = await post.render(); --- <h1>{post.data.title}</h1> <p>{post.data.description}</p> <Content />
动态路由与内容集合:
astro--- // src/pages/blog/[slug].astro import { getCollection } from 'astro:content'; export async function getStaticPaths() { const posts = await getCollection('blog'); return posts.map(post => ({ params: { slug: post.slug }, props: { post }, })); } const { post } = Astro.props; const { Content } = await post.render(); --- <h1>{post.data.title}</h1> <Content />
使用 MDX:
markdown--- title: "使用 MDX 的文章" description: "支持 JSX 的 Markdown" publishDate: 2024-01-20 tags: ["mdx", "astro"] --- import { Counter } from '../../components/Counter.jsx'; 这是普通的 Markdown 内容。 <Counter /> 你可以在这里使用任何组件!
内容集合的优势:
-
类型安全:
- 使用 Zod schema 定义内容结构
- TypeScript 自动推断类型
- 编译时验证
-
性能优化:
- 构建时处理内容
- 自动生成路由
- 避免运行时解析
-
开发体验:
- IDE 自动完成
- 类型检查
- 错误提示
-
灵活性:
- 支持 Markdown、MDX、JSON、YAML
- 自定义 schema
- 前置数据处理
高级用法:
-
嵌套目录:
shellsrc/content/ ├── blog/ │ ├── 2024/ │ │ ├── january/ │ │ │ └── post.md │ │ └── february/ │ │ └── post.md │ └── 2023/ │ └── post.md └── docs/ └── guide.md -
自定义渲染器:
typescript// src/content/config.ts import { defineCollection, z } from 'astro:content'; const blog = defineCollection({ type: 'content', schema: z.object({ title: z.string(), }), // 自定义渲染器 render: async ({ entry }) => { // 自定义渲染逻辑 return entry.render(); }, }); -
内容转换:
typescript// src/content/config.ts const blog = defineCollection({ type: 'content', schema: z.object({ title: z.string(), date: z.coerce.date(), }), transform: async (data, id) => { // 转换数据 return { ...data, slug: id.replace('.md', ''), }; }, });
最佳实践:
- 使用 Zod schema 定义清晰的内容结构
- 为不同的内容类型创建单独的集合
- 利用 TypeScript 获得类型安全
- 使用
getStaticPaths生成动态路由 - 在构建时处理所有内容,避免运行时开销
内容集合是 Astro 处理结构化内容的最佳方式,特别适合博客、文档、产品目录等内容驱动的网站。