集合入门 #
一、什么是内容集合? #
1.1 概念介绍 #
内容集合(Content Collections)是 Astro 内置的内容管理系统,用于组织、验证和管理 Markdown、MDX 等内容文件。
text
┌─────────────────────────────────────────────────────┐
│ 内容集合工作流程 │
├─────────────────────────────────────────────────────┤
│ │
│ 内容文件 集合配置 页面 │
│ ┌─────────┐ ┌─────────┐ ┌─────┐│
│ │ post.md │ │ │ │ ││
│ │ post2.md│ ──查询──► │ config │ ──渲染─► │页面 ││
│ │ post3.md│ │ │ │ ││
│ └─────────┘ └─────────┘ └─────┘│
│ │
└─────────────────────────────────────────────────────┘
1.2 核心优势 #
| 优势 | 说明 |
|---|---|
| 类型安全 | 自动生成 TypeScript 类型 |
| Schema 验证 | 确保内容格式正确 |
| 智能提示 | IDE 自动补全支持 |
| 内容组织 | 按类型分组管理 |
| 高效查询 | 内置查询 API |
二、目录结构 #
2.1 基本结构 #
text
src/
├── content/
│ ├── config.ts # 集合配置文件
│ ├── blog/ # 博客集合
│ │ ├── post-1.md
│ │ ├── post-2.md
│ │ └── post-3.md
│ └── docs/ # 文档集合
│ ├── intro.md
│ └── guide.md
└── pages/
└── blog/
└── [...slug].astro
2.2 创建内容目录 #
bash
# 创建内容目录
mkdir -p src/content/blog
mkdir -p src/content/docs
# 创建配置文件
touch src/content/config.ts
三、创建集合配置 #
3.1 基本配置 #
typescript
// src/content/config.ts
import { defineCollection, z } from 'astro:content';
const blogCollection = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
description: z.string(),
date: z.date(),
draft: z.boolean().default(false),
}),
});
export const collections = {
blog: blogCollection,
};
3.2 完整 Schema 定义 #
typescript
// src/content/config.ts
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
type: 'content',
schema: ({ image }) => z.object({
title: z.string().min(1).max(100),
description: z.string().min(10).max(200),
date: z.coerce.date(),
updated: z.coerce.date().optional(),
author: z.string().default('匿名'),
image: image().optional(),
imageAlt: z.string().optional(),
tags: z.array(z.string()).default([]),
categories: z.array(z.string()).default([]),
draft: z.boolean().default(false),
featured: z.boolean().default(false),
toc: z.boolean().default(true),
}),
});
const docs = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
description: z.string(),
order: z.number().default(0),
section: z.string().optional(),
}),
});
const authors = defineCollection({
type: 'data',
schema: z.object({
name: z.string(),
avatar: z.string().optional(),
bio: z.string().optional(),
twitter: z.string().optional(),
github: z.string().optional(),
}),
});
export const collections = {
blog,
docs,
authors,
};
四、创建内容文件 #
4.1 Markdown 文件 #
markdown
---
title: Astro
description: 了解 Astro 内容集合的概念、优势以及如何开始使用内容集合管理网站内容。
date: 2024-01-15
author: 张三
tags:
- Astro
- 前端
categories:
- 教程
draft: false
featured: true
---
# Astro 入门指南
Astro 是一款现代化的静态站点生成器...
## 为什么选择 Astro?
Astro 具有以下优势:
- 零 JavaScript 默认
- Islands 架构
- 框架无关
4.2 数据文件(JSON) #
json
// src/content/authors/zhangsan.json
{
"name": "张三",
"avatar": "/avatars/zhangsan.jpg",
"bio": "前端开发工程师",
"twitter": "zhangsan",
"github": "zhangsan"
}
4.3 数据文件(YAML) #
yaml
# src/content/authors/lisi.yaml
name: 李四
avatar: /avatars/lisi.jpg
bio: 全栈开发工程师
twitter: lisi
github: lisi
五、查询内容 #
5.1 获取所有条目 #
astro
---
// src/pages/blog/index.astro
import { getCollection } from 'astro:content';
const posts = await getCollection('blog', ({ data }) => {
return !data.draft;
});
---
<div>
{posts.map(post => (
<article>
<h2>{post.data.title}</h2>
<p>{post.data.description}</p>
<time>{post.data.date.toLocaleDateString('zh-CN')}</time>
</article>
))}
</div>
5.2 获取单个条目 #
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 render(post);
---
<article>
<h1>{post.data.title}</h1>
<p>{post.data.description}</p>
<Content />
</article>
5.3 过滤和排序 #
astro
---
import { getCollection } from 'astro:content';
const posts = await getCollection('blog', ({ data }) => {
return !data.draft;
});
const sortedPosts = posts.sort((a, b) =>
b.data.date.valueOf() - a.data.date.valueOf()
);
const featuredPosts = posts.filter(post => post.data.featured);
---
<div>
<section>
<h2>精选文章</h2>
{featuredPosts.map(post => (
<article key={post.slug}>
<h3>{post.data.title}</h3>
</article>
))}
</section>
<section>
<h2>所有文章</h2>
{sortedPosts.map(post => (
<article key={post.slug}>
<h3>{post.data.title}</h3>
<time>{post.data.date.toLocaleDateString('zh-CN')}</time>
</article>
))}
</section>
</div>
六、渲染内容 #
6.1 基本渲染 #
astro
---
import { getEntry, render } from 'astro:content';
const post = await getEntry('blog', 'hello-world');
const { Content } = await render(post);
---
<article>
<h1>{post.data.title}</h1>
<Content />
</article>
6.2 获取渲染信息 #
astro
---
import { getEntry, render } from 'astro:content';
const post = await getEntry('blog', 'hello-world');
const { Content, headings, remarkPluginFrontmatter } = await render(post);
---
<article>
<h1>{post.data.title}</h1>
<aside class="toc">
<h2>目录</h2>
<ul>
{headings.map(heading => (
<li key={heading.slug}>
<a href={`#${heading.slug}`}>{heading.text}</a>
</li>
))}
</ul>
</aside>
<Content />
</article>
七、类型安全 #
7.1 自动类型推断 #
typescript
// 类型自动从 schema 推断
import { getCollection } from 'astro:content';
const posts = await getCollection('blog');
// posts 类型自动推断为:
// {
// id: string;
// slug: string;
// data: {
// title: string;
// description: string;
// date: Date;
// ...
// };
// body: string;
// collection: 'blog';
// }[]
7.2 使用类型 #
typescript
// src/types/content.ts
import type { CollectionEntry } from 'astro:content';
type BlogPost = CollectionEntry<'blog'>;
type Doc = CollectionEntry<'docs'>;
type Author = CollectionEntry<'authors'>;
export type { BlogPost, Doc, Author };
astro
---
import type { BlogPost } from '../types/content';
interface Props {
post: BlogPost;
}
const { post } = Astro.props;
---
<article>
<h1>{post.data.title}</h1>
</article>
八、最佳实践 #
8.1 组织内容 #
text
src/content/
├── config.ts
├── blog/
│ ├── 2024/
│ │ ├── 01/
│ │ │ └── hello-world.md
│ │ └── 02/
│ │ └── astro-guide.md
│ └── 2023/
│ └── getting-started.md
├── docs/
│ ├── intro/
│ │ ├── index.md
│ │ └── installation.md
│ └── guide/
│ ├── basics.md
│ └── advanced.md
└── authors/
├── zhangsan.json
└── lisi.yaml
8.2 Schema 设计 #
typescript
// 好的 Schema 设计
const blog = defineCollection({
schema: z.object({
// 必填字段
title: z.string().min(1, '标题不能为空'),
description: z.string().min(10, '描述至少10个字符'),
date: z.coerce.date(),
// 可选字段带默认值
draft: z.boolean().default(false),
featured: z.boolean().default(false),
author: z.string().default('匿名'),
// 数组字段
tags: z.array(z.string()).default([]),
// 条件字段
image: z.string().optional(),
imageAlt: z.string().optional(),
}).refine(
data => !data.image || data.imageAlt,
{ message: '有图片时必须提供 alt 文本' }
),
});
九、总结 #
内容集合核心要点:
text
┌─────────────────────────────────────────────────────┐
│ 内容集合要点 │
├─────────────────────────────────────────────────────┤
│ │
│ 📁 目录结构 src/content/ 存放内容 │
│ │
│ ⚙️ 配置文件 config.ts 定义集合和 schema │
│ │
│ 📝 内容文件 Markdown/JSON/YAML 格式 │
│ │
│ 🔍 查询 API getCollection/getEntry │
│ │
│ 🎨 渲染内容 render() 获取组件 │
│ │
│ ✅ 类型安全 自动生成 TypeScript 类型 │
│ │
└─────────────────────────────────────────────────────┘
下一步,让我们学习 定义集合,深入了解集合配置!
最后更新:2026-03-28