前言
除了在组件中定义数据、在 src 中 import 进来或者 fetch 远端数据之外,还有其他撰写内容的方式吗?有的!
什么是内容集合 Content Collection?
内容集合在 2.0 版本推出,用于为网站的本地内容提供易于使用的管理、自动化类型验证功能。如果你有大量文件需要注入网页中便可以使用该功能。
怎么使用内容集合?
在介绍资料夹结构的章节中提到 src/content 是被保留的资料夹,其用途是用于存放内容集合相关的文件与設定,此外不能用做其他用途。首先可以创造一个条目资料夹,并且在里面存放与该条目相关的文件,文件可以是 Markdown 或 MDX 甚至是 YAML 或 JSON:
src/content/├── newsletter/│ ├── week-1.md│ └── week-2.md├── blog/│ ├── post-1.md│ └── post-2.md└── authors/ ├── grace-hopper.json └── alan-turing.json当创建好条目与其内容文件之后就可以透过 Astro 提供的 API 查询。
什么是 .astro 资料夹?
在使用内容集合时会发现 Astro 会为内容集合的设定自动生成相关的档案,这些档案会被放置在 .astro 资料夹中,不需要做任何的设定或修改,只要执行 astro dev或 astro build 就会自动生成,也可以手动执行 astro sync 来生成。如果你有使用 Git 管理专案,建议将该资料夹给写入 .gitignore 设定当中,避免被记录下来。
定义集合
内容集合的设定是可选的,增加额外的集合设定将会更好的帮助 Astro 验证资料的型别。要定义集合就需要创建 src/content/config.ts 文件(也可以是 .js 或 .mjs),基本内容如下:
// 1. 导入 `astro:content` 提供的工具函式import { defineCollection } from 'astro:content';// 2. 定义集合const blogCollection = defineCollection({ /* ... */});// 3. 输出一个 `collections` 物件用于注册集合,名称应与条目资料夹相同export const collections = { blog: blogCollection,};透过定义集合型别可以强制集合内资料的准确与一致性,当有违背规则的情况 Astro 将会提供错误与建议,以下是一个集合物件的范例:
import { z, defineCollection } from 'astro:content';
defineCollection({ type: 'content', // v2.5.0 版本新增,注明资料种类是 Markdown 还是像 JSON 或 YAML 的格式(content / data) schema: z.object({ title: z.string(), tags: z.array(z.string()), image: z.string().optional(), }),});当集合内的资料是 Markdown 时 type 为 content,当是 JSON 或 YAML 时则是 data。 schema 的设置可以参考 Zod 文件,Astro 使用 Zod 来为资料做检核。
定义多个集合
可以创造多个集合之后再放入 collections 物件中。
const blogCollection = defineCollection({ type: 'content', schema: z.object({ /* ... */ }),});const newsletter = defineCollection({ type: 'content', schema: z.object({ /* ... */ }),});const authors = defineCollection({ type: 'data', schema: z.object({ /* ... */ }),});
export const collections = { blog: blogCollection, newsletter: newsletter, authors: authors,};查询集合
Astro 提供两个函式用于查询一个或多个内容集合,分别是:getCollection 与 getEntry。
import { getCollection, getEntry } from 'astro:content';
// 获取所有内容集合// 需要集合的名称作为参数// 举例来说: `src/content/blog/**`const allBlogPosts = await getCollection('blog');
// 获取单个条目从集合之中// 需要集合的名称以及条目 `slug` (内容集合)或 `id`(资料集合)const graceHopperProfile = await getEntry('authors', 'grace-hopper');筛选集合
getCollection 接受一个「过滤用」的回呼函式,用于过滤搜寻内容,像以下的案例:「当在 Production 环境时,如果该笔项目 draft 非为 true才显示,在非 Production 则显示一切资料」
import { getCollection } from 'astro:content';const blogEntries = await getCollection('blog', ({ data }) => { return import.meta.env.PROD ? data.draft !== true : true;});显示集合内容
透过 map 来遍历并回传每个项目的 Markup,我们能将捞取到的资料制作成一个清单并呈现到页面当中。
---import { getCollection } from 'astro:content';const blogEntries = await getCollection('blog');---<ul> {blogEntries.map(blogPostEntry => ( <li> <a href={`/my-blog-url/${blogPostEntry.slug}`}>{blogPostEntry.data.title}</a> <time datetime={blogPostEntry.data.publishedDate.toISOString()}> {blogPostEntry.data.publishedDate.toDateString()} </time> </li> ))}</ul>运用集合内容产生 SSG Route
一样透过 getStaticPaths 这个方法,不过这次使用的资料不像之前一样是写死在元件内的,而是透过集合内容的资料来建立。
---import { getCollection } from 'astro:content';
export async function getStaticPaths() { // 1. 抓取 blog 集合 const blogEntries = await getCollection('blog'); // 2. 回传个别的 Route 名称与 Props 用于产生 Route return blogEntries.map(entry => ({ params:{ slug: entry.slug }, props: { entry }, }));}// 3. 再透过解构 Props 得到个别集合项目中的内容并显示出来const { entry } = Astro.props;const { Content } = await entry.render();---<h1>{entry.data.title}</h1><Content />总结
最后会建议实际动手练习,如果过程中有问题可以参考看看我的范例:
- 使用 Astro 提供的
defineCollection来创建自己理想的集合并定义其资料型态 - 在
content资料夹中撰写建立相关档案 - 使用
getCollection抓取并显示在页面当中 - 使用
getCollection生成 Route
延伸阅读
Content Collections - Astro DOCS
- Day16 - 内容集合 - 相同文章同步发布于 iThome 铁人赛中