集合入门 #

一、什么是内容集合? #

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