From 227dccf25701deb48daa514db035acab9c49bdeb Mon Sep 17 00:00:00 2001 From: Flame-Y <94735231+Flame-Y@users.noreply.github.com> Date: Thu, 26 Oct 2023 14:02:05 +0800 Subject: [PATCH 1/3] Create add-content-collections.mdx --- .../tutorials/add-content-collections.mdx | 384 ++++++++++++++++++ 1 file changed, 384 insertions(+) create mode 100644 src/content/docs/zh-cn/tutorials/add-content-collections.mdx diff --git a/src/content/docs/zh-cn/tutorials/add-content-collections.mdx b/src/content/docs/zh-cn/tutorials/add-content-collections.mdx new file mode 100644 index 0000000000000..aa096b6d167fd --- /dev/null +++ b/src/content/docs/zh-cn/tutorials/add-content-collections.mdx @@ -0,0 +1,384 @@ +--- +title: 教程 - 在项目中使用内容集合 +description: >- + 将构建博客教程代码从基于文件的路由转换为内容集合(Content Collections) +--- +import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'; +import Box from '~/components/tutorial/Box.astro'; +import MultipleChoice from '~/components/tutorial/MultipleChoice.astro'; +import PreCheck from '~/components/tutorial/PreCheck.astro'; +import Option from '~/components/tutorial/Option.astro'; + +**内容集合** 是一种管理相似内容(如博客文章)的有效方法。内容集合帮助管理你的文档,校验 frontmatter,并为所有内容提供自动 TypeScript 类型安全(即使你没有编写任何 TypeScript)。 + + + - 将包含博客文章的文件夹移入 `src/content/` + - 创建一个模式来定义你的博客文章的 frontmatter + - 使用 `getCollection()` 获取博客文章内容和元数据 + + +## Prerequisites + +你需要准备一个现成的 Astro 项目,里面存在包含 Markdown 或者 MDX 类型文件的 `src/pages/` 文件夹. + +本教程使用先前[搭建博客教程完成后的项目代码](https://github.com/withastro/blog-tutorial-demo)来演示如何将博客转化为内容集合。你可以在本地克隆并使用该代码库,或者通过于 [StackBlitz 上编辑教程的代码](https://stackblitz.com/github/withastro/blog-tutorial-demo/tree/complete?file=src%2Fpages%2Findex.astro)在浏览器中完成该教程。 + +你也可以在自己的项目中复现这些步骤,但是需要根据自己的代码库调整操作步骤。 + +我们推荐你使用我们的示例项目来完成这个简短的教程。然后,你就可以使用在这篇教程中所学的知识来给你自己的项目添加内容集合。 + +## 教程的示例代码 + +在[搭建博客教程](/zh-cn/tutorial/0-introduction/)中,你了解到 Astro 的基于文件的路由会将任何放在 `src/pages/` 文件夹中的 `.astro`、`.md` 或 `.mdx` 文件自动变成你网站上的一个新页面。 + +为了在 `https://example.com/posts/post-1/` 创建第一篇博客文章, 你创建了 `/posts/` 文件夹并添加了 `post-1.md` 文件。然后,每当你想要添加新的博客文章到网站时,都要向该文件夹添加一个新的 Markdown 文件。 + + +## 页面(Pages) vs 集合(Collections) + +即使使用内容集合, 你仍然可以使用 `src/pages/` 文件夹来存放独立的页面, 比如关于页面。但是, 将你的博客文章迁移到 `src/content/` 文件夹将允许你使用更强大和性能更好的 API 来生成你的博客文章索引和展示你的个人博客文章。 + +同时, 你将在代码编辑器中获得更好的引导和自动完成功能,因为你将会有一个**[模式](/zh-cn/guides/content-collections/#定义集合模式)** 来为每篇文章定义一个通用的结构,Astro将帮助你执行该结构。 In your schema, you can specify when frontmatter properties are required, such as a description or an author, and which data type each property must be, such as a string or an array. This leads to catching many mistakes sooner, with descriptive error messages telling you exactly what the problem is. + +在我们的指南中阅读更多关于 [Astro 的内容集合](/zh-cn/guides/content-collections/) 的信息,或者按照下面的说明将基础博客从 `src/pages/posts/` 转换为 `src/content/posts/` 来开始操作。 + + +### 小测试 + +1. 你可能会在 `src/pages/` 中保留哪种类型的页面? + + + + + + + +2. 将博客文章迁移到内容集合的好处 **不包含** 什么? + + + + + + + +3. 内容集合使用 TypeScript... + + + + + + + + +## 在示例博客代码中使用内容集合 + +通过下列步骤你将了解:如何通过为博客文章创建内容集合,用以扩展实例博客。 + +### 更新依赖 + +1. 在终端中运行以下命令,将 Astro 升级到最新版本,并将所有集成升级到它们的最新版本: + + + + ```shell + # 升级到 Astro v3.x + npm install astro@latest + + # 例: 升级博客教程中的 Preact 集成 + npm install @astrojs/preact@latest + ``` + + + ```shell + # 升级到 Astro v3.x + pnpm install astro@latest + + # 例:升级博客教程中的 Preact 集成 + pnpm install @astrojs/preact@latest + ``` + + + ```shell + # 升级到 Astro v3.x + yarn add astro@latest + + # 例:升级博客教程中的 Preact 集成 + yarn add @astrojs/preact@latest + ``` + + + + :::tip + 如果你正在使用自己的项目,请确保你已更新所有已安装的依赖项。搭建博客教程的示例代码库仅使用了 Preact 集成。 + ::: + +2. 博客教程使用 `base` (最不严格的) TypeScript 设置。为了使用内容集合, 你必须 [设置 TypeScript](/zh-cn/guides/content-collections/#设置-typescript) 使用 `strict` 或 `strictest` 的设置, **或者** 在 `tsconfig.json` 添加两个选项。 + + 为了在博客教程示例的其余部分使用内容集合而不编写 TypeScript,请在配置文件中添加以下两个 TypeScript 配置选项: + + ```json title="tsconfig.json" ins={5,6} + { + // 注意: 如果你使用 "astro/tsconfigs/strict" 或 "astro/tsconfigs/strictest",则不需要更改 + "extends": "astro/tsconfigs/base", + "compilerOptions": { + "strictNullChecks": true, + "allowJs": true + } + } + ``` + +### 为你的博客文章创建一个集合 + +3. 创建一个名为 `src/content/posts/` 的新 **集合** (文件夹)。 + +4. 将现有的所有博客文章 (`.md` 文件) 从 `src/pages/posts/` 移到这个新集合中。 + +5. 创建一个 `src/content/config.ts` 文件用来为 `postsCollection` [定义模式](/zh-cn/guides/content-collections/#定义集合模式)。对于现有的博客教程代码,请将以下内容添加到文件中,以定义其博客文章中使用的所有frontmatter属性: + + ```ts title="src/content/config.ts" + // Import utilities from `astro:content` + import { z, defineCollection } from "astro:content"; + // Define a `type` and `schema` for each collection + const postsCollection = defineCollection({ + type: 'content', + schema: z.object({ + title: z.string(), + pubDate: z.date(), + description: z.string(), + author: z.string(), + image: z.object({ + url: z.string(), + alt: z.string() + }), + tags: z.array(z.string()) + }) + }); + // Export a single `collections` object to register your collection(s) + export const collections = { + posts: postsCollection, + }; + ``` + +### 从集合中生成页面 + +6. 创建一个名为 `src/pages/posts/[...slug].astro` 的页面文件。 当Markdown和MDX文件位于集合中时,它们不再使用Astro基于文件的路由自动成为页面,因此必须创建一个用来生成每个独立的博客文章的页面。 + +7. 添加以下代码到 [查询集合](/zh-cn/guides/content-collections/#查询集合) 中,以使每篇博客文章的 slug 和页面内容在生成的每个页面中都可用: + + ```astro title="src/pages/posts/[...slug].astro" + --- + import { getCollection } from 'astro:content'; + import MarkdownPostLayout from '../../layouts/MarkdownPostLayout.astro'; + + export async function getStaticPaths() { + const blogEntries = await getCollection('posts'); + return blogEntries.map(entry => ({ + params: { slug: entry.slug }, props: { entry }, + })); + } + + const { entry } = Astro.props; + const { Content } = await entry.render(); + --- + ``` + +8. 在Markdown页面的布局中渲染你的文章 ``。这使你能够为所有文章指定一个通用布局。 + + ```astro title="src/pages/posts/[...slug].astro" ins={15-17} + --- + import { getCollection } from 'astro:content'; + import MarkdownPostLayout from '../../layouts/MarkdownPostLayout.astro'; + + export async function getStaticPaths() { + const blogEntries = await getCollection('posts'); + return blogEntries.map(entry => ({ + params: { slug: entry.slug }, props: { entry }, + })); + } + + const { entry } = Astro.props; + const { Content } = await entry.render(); + --- + + + + ``` + +9. 删除每篇博客在 frontmatter 中定义的 `layout`。现在,你的内容在渲染后被包装在布局中,不再需要此属性。 + + ```md title="src/content/posts/post-1.md" del={2} + --- + layout: ../../layouts/MarkdownPostLayout.astro + title: 'My First Blog Post' + pubDate: 2022-07-01 + ... + --- + ``` + +### 使用 `getCollection()` 替换 `Astro.glob()` + +10. 任何有博客文章列表的地方,比如教程的 Blog 页面 (`src/pages/blog.astro/`),都需要使用 [`getCollection()`](/zh-cn/reference/api-reference/#getcollection) 替换 `Astro.glob()` ,作为从 Markdown 文件中获取内容和元数据的方法。 + + ```astro title="src/pages/blog.astro" "post.data" "getCollection(\"posts\")" "/posts/${post.slug}/" del={7} ins={2,8} + --- + import { getCollection } from "astro:content"; + import BaseLayout from "../layouts/BaseLayout.astro"; + import BlogPost from "../components/BlogPost.astro"; + + const pageTitle = "My Astro Learning Blog"; + const allPosts = await Astro.glob("../pages/posts/*.md"); + const allPosts = await getCollection("posts"); + --- + ``` + +11. 你还需要更新对每个 `post` 返回值的引用。现在,你能够在每个对象的 `data` 属性中获得 frontmatter 值。此外,当使用集合时,每个 `post` 对象将有一个 `slug` 页面,而不是完整的 URL. + + ```astro title="src/pages/blog.astro" "data" "/posts/$\{post.slug\}/" del={14} ins={15} + --- + import { getCollection } from "astro:content"; + import BaseLayout from "../layouts/BaseLayout.astro"; + import BlogPost from "../components/BlogPost.astro"; + + const pageTitle = "My Astro Learning Blog"; + const allPosts = await getCollection("posts"); + --- + +

This is where I will post about my journey learning Astro.

+ +
+ ``` + +12. 教程博客项目还使用 `src/pages/tags/[tag].astro` 为每个标签动态地生成一个页面,并在 `src/pages/tags/index.astro` 中显示标签列表。 + + 对这两个文件应用与上述相同的更改: + + - 使用 `getCollection("posts")` 代替 `Astro.glob()` 来获取所有博客文章的数据 + - 使用 `data` 代替 `frontmatter` 来获取所有博客文章的数据 + - 通过将文章的 `slug` 添加到 `/posts/` 路径来创建页面URL + + 生成单个标签页的页面现在变为: + + ```astro title="src/pages/tags/[tag].astro" "post.data.tags" "getCollection(\"posts\")" "post.data.title" ins={2} "/posts/${post.slug}/" + --- + import { getCollection } from "astro:content"; + import BaseLayout from "../../layouts/BaseLayout.astro"; + import BlogPost from "../../components/BlogPost.astro"; + + export async function getStaticPaths() { + const allPosts = await getCollection("posts"); + const uniqueTags = [...new Set(allPosts.map((post) => post.data.tags).flat())]; + + return uniqueTags.map((tag) => { + const filteredPosts = allPosts.filter((post) => + post.data.tags.includes(tag) + ); + return { + params: { tag }, + props: { posts: filteredPosts }, + }; + }); + } + + const { tag } = Astro.params; + const { posts } = Astro.props; + --- + + +

Posts tagged with {tag}

+ +
+ ``` + + + ### 小试牛刀 - 更新标记索引页中的查询 + + 按照[上述相同的步骤](#使用-getcollection-替换-astroglob)导入并使用 `getCollection` 来获取在博客文章中使用过的标签以展示在 `src/pages/tags/index.astro` 页面。 + +
+ Show me the code. + ```astro title="src/pages/tags/index.astro" "post.data" "getCollection(\"posts\")" ins={2} + --- + import { getCollection } from "astro:content"; + import BaseLayout from "../../layouts/BaseLayout.astro"; + const allPosts = await getCollection("posts"); + const tags = [...new Set(allPosts.map((post) => post.data.tags).flat())]; + const pageTitle = "Tag Index"; + --- + ... + ``` +
+
+ +### 更新所有 frontmatter 值以匹配模式 + +13. 如有必要,请在整个项目(例如布局中)更新与集合模式不匹配的任何 frontmatter 值。 + + 在博客教程示例中,`pubDate` 是一个字符串。 现在,根据模式中为文章 frontmatter 定义的类型, `pubDate` 将是一个 `Date` 对象。 + + 要在博客文章布局中渲染日期,请将其转换为字符串: + + ```astro title="src/layouts/MarkdownPostLayout.astro" ins="toString()" + ... + +

{frontmatter.pubDate.toString().slice(0,10)}

+

{frontmatter.description}

+

Written by: {frontmatter.author}

+ {frontmatter.image.alt} + ... + ``` + +### 更新 RSS 函数 + +14. 最后,教程博客项目包括一个 RSS 提要。该函数必须使用 `getCollection()` 来返回博客文章中的信息。然后,你可以使用返回的 `data` 对象生成 RSS 项。 + + ```js title="src/pages/rss.xml.js" del={2,11} ins={3,6,12-17} + import rss from '@astrojs/rss'; + import { pagesGlobToRssItems } from '@astrojs/rss'; + import { getCollection } from 'astro:content'; + + export async function GET(context) { + const posts = await getCollection("posts"); + return rss({ + title: 'Astro Learner | Blog', + description: 'My journey learning Astro', + site: context.site, + items: await pagesGlobToRssItems(import.meta.glob('./**/*.md')), + items: posts.map((post) => ({ + title: post.data.title, + pubDate: post.data.pubDate, + description: post.data.description, + link: `/posts/${post.slug}/`, + })), + customData: `en-us`, + }) + } + ``` + +要查看使用内容集合的博客教程的完整示例,请访问教程存储库的 [Content Collections 分支](https://github.com/withastro/blog-tutorial-demo/tree/content-collections)。 From ddd48f011b0693692c67730cc1c50c9e8ff1d818 Mon Sep 17 00:00:00 2001 From: Flame-Y <94735231+Flame-Y@users.noreply.github.com> Date: Thu, 26 Oct 2023 15:09:04 +0800 Subject: [PATCH 2/3] Update add-content-collections.mdx --- .../docs/zh-cn/tutorials/add-content-collections.mdx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/content/docs/zh-cn/tutorials/add-content-collections.mdx b/src/content/docs/zh-cn/tutorials/add-content-collections.mdx index aa096b6d167fd..7fce963b91775 100644 --- a/src/content/docs/zh-cn/tutorials/add-content-collections.mdx +++ b/src/content/docs/zh-cn/tutorials/add-content-collections.mdx @@ -17,9 +17,9 @@ import Option from '~/components/tutorial/Option.astro'; - 使用 `getCollection()` 获取博客文章内容和元数据 -## Prerequisites +## 先决条件 -你需要准备一个现成的 Astro 项目,里面存在包含 Markdown 或者 MDX 类型文件的 `src/pages/` 文件夹. +你需要准备一个现成的 Astro 项目,里面存在包含 Markdown 或者 MDX 类型文件的 `src/pages/` 文件夹。 本教程使用先前[搭建博客教程完成后的项目代码](https://github.com/withastro/blog-tutorial-demo)来演示如何将博客转化为内容集合。你可以在本地克隆并使用该代码库,或者通过于 [StackBlitz 上编辑教程的代码](https://stackblitz.com/github/withastro/blog-tutorial-demo/tree/complete?file=src%2Fpages%2Findex.astro)在浏览器中完成该教程。 @@ -34,11 +34,11 @@ import Option from '~/components/tutorial/Option.astro'; 为了在 `https://example.com/posts/post-1/` 创建第一篇博客文章, 你创建了 `/posts/` 文件夹并添加了 `post-1.md` 文件。然后,每当你想要添加新的博客文章到网站时,都要向该文件夹添加一个新的 Markdown 文件。 -## 页面(Pages) vs 集合(Collections) +## 页面(Pages)vs 集合(Collections) 即使使用内容集合, 你仍然可以使用 `src/pages/` 文件夹来存放独立的页面, 比如关于页面。但是, 将你的博客文章迁移到 `src/content/` 文件夹将允许你使用更强大和性能更好的 API 来生成你的博客文章索引和展示你的个人博客文章。 -同时, 你将在代码编辑器中获得更好的引导和自动完成功能,因为你将会有一个**[模式](/zh-cn/guides/content-collections/#定义集合模式)** 来为每篇文章定义一个通用的结构,Astro将帮助你执行该结构。 In your schema, you can specify when frontmatter properties are required, such as a description or an author, and which data type each property must be, such as a string or an array. This leads to catching many mistakes sooner, with descriptive error messages telling you exactly what the problem is. +同时, 你将在代码编辑器中获得更好的引导和自动完成功能,因为你将会有一个**[模式](/zh-cn/guides/content-collections/#定义集合模式)** 来为每篇文章定义一个通用的结构,Astro将帮助你执行该结构。在模式中,你可以指定什么时候需要 fronmatter 属性(例如说明或作者),以及每个属性必须是哪种数据类型(例如字符串或数组)。这样做可以更早地发现许多错误,并提供详细的错误信息,准确地告诉你问题所在。 在我们的指南中阅读更多关于 [Astro 的内容集合](/zh-cn/guides/content-collections/) 的信息,或者按照下面的说明将基础博客从 `src/pages/posts/` 转换为 `src/content/posts/` 来开始操作。 @@ -249,7 +249,7 @@ import Option from '~/components/tutorial/Option.astro'; --- ``` -11. 你还需要更新对每个 `post` 返回值的引用。现在,你能够在每个对象的 `data` 属性中获得 frontmatter 值。此外,当使用集合时,每个 `post` 对象将有一个 `slug` 页面,而不是完整的 URL. +11. 你还需要更新对每个 `post` 返回值的引用。现在,你能够在每个对象的 `data` 属性中获得 frontmatter 值。此外,当使用集合时,每个 `post` 对象将有一个 `slug` 页面,而不是完整的 URL。 ```astro title="src/pages/blog.astro" "data" "/posts/$\{post.slug\}/" del={14} ins={15} --- @@ -322,7 +322,7 @@ import Option from '~/components/tutorial/Option.astro'; 按照[上述相同的步骤](#使用-getcollection-替换-astroglob)导入并使用 `getCollection` 来获取在博客文章中使用过的标签以展示在 `src/pages/tags/index.astro` 页面。
- Show me the code. + 给我看看代码! ```astro title="src/pages/tags/index.astro" "post.data" "getCollection(\"posts\")" ins={2} --- import { getCollection } from "astro:content"; From ca865dd4724955ba294546a83029bcdaca3b2acf Mon Sep 17 00:00:00 2001 From: Flame-Y <94735231+Flame-Y@users.noreply.github.com> Date: Thu, 26 Oct 2023 15:25:17 +0800 Subject: [PATCH 3/3] Update add-content-collections.mdx fix some format problem --- .../docs/zh-cn/tutorials/add-content-collections.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/content/docs/zh-cn/tutorials/add-content-collections.mdx b/src/content/docs/zh-cn/tutorials/add-content-collections.mdx index 7fce963b91775..3f7f6ac72f76b 100644 --- a/src/content/docs/zh-cn/tutorials/add-content-collections.mdx +++ b/src/content/docs/zh-cn/tutorials/add-content-collections.mdx @@ -38,7 +38,7 @@ import Option from '~/components/tutorial/Option.astro'; 即使使用内容集合, 你仍然可以使用 `src/pages/` 文件夹来存放独立的页面, 比如关于页面。但是, 将你的博客文章迁移到 `src/content/` 文件夹将允许你使用更强大和性能更好的 API 来生成你的博客文章索引和展示你的个人博客文章。 -同时, 你将在代码编辑器中获得更好的引导和自动完成功能,因为你将会有一个**[模式](/zh-cn/guides/content-collections/#定义集合模式)** 来为每篇文章定义一个通用的结构,Astro将帮助你执行该结构。在模式中,你可以指定什么时候需要 fronmatter 属性(例如说明或作者),以及每个属性必须是哪种数据类型(例如字符串或数组)。这样做可以更早地发现许多错误,并提供详细的错误信息,准确地告诉你问题所在。 +同时, 你将在代码编辑器中获得更好的引导和自动完成功能,因为你将会有一个[模式](/zh-cn/guides/content-collections/#定义集合模式)来为每篇文章定义一个通用的结构,Astro将帮助你执行该结构。在模式中,你可以指定什么时候需要 fronmatter 属性(例如说明或作者),以及每个属性必须是哪种数据类型(例如字符串或数组)。这样做可以更早地发现许多错误,并提供详细的错误信息,准确地告诉你问题所在。 在我们的指南中阅读更多关于 [Astro 的内容集合](/zh-cn/guides/content-collections/) 的信息,或者按照下面的说明将基础博客从 `src/pages/posts/` 转换为 `src/content/posts/` 来开始操作。 @@ -130,7 +130,7 @@ import Option from '~/components/tutorial/Option.astro'; 如果你正在使用自己的项目,请确保你已更新所有已安装的依赖项。搭建博客教程的示例代码库仅使用了 Preact 集成。 ::: -2. 博客教程使用 `base` (最不严格的) TypeScript 设置。为了使用内容集合, 你必须 [设置 TypeScript](/zh-cn/guides/content-collections/#设置-typescript) 使用 `strict` 或 `strictest` 的设置, **或者** 在 `tsconfig.json` 添加两个选项。 +2. 博客教程使用 `base` (最不严格的) TypeScript 设置。为了使用内容集合,你必须 [设置 TypeScript](/zh-cn/guides/content-collections/#设置-typescript) 使用 `strict` 或 `strictest` 的设置,**或者** 在 `tsconfig.json` 添加两个选项。 为了在博客教程示例的其余部分使用内容集合而不编写 TypeScript,请在配置文件中添加以下两个 TypeScript 配置选项: @@ -179,9 +179,9 @@ import Option from '~/components/tutorial/Option.astro'; ### 从集合中生成页面 -6. 创建一个名为 `src/pages/posts/[...slug].astro` 的页面文件。 当Markdown和MDX文件位于集合中时,它们不再使用Astro基于文件的路由自动成为页面,因此必须创建一个用来生成每个独立的博客文章的页面。 +6. 创建一个名为 `src/pages/posts/[...slug].astro` 的页面文件。当Markdown和MDX文件位于集合中时,它们不再使用Astro基于文件的路由自动成为页面,因此必须创建一个用来生成每个独立的博客文章的页面。 -7. 添加以下代码到 [查询集合](/zh-cn/guides/content-collections/#查询集合) 中,以使每篇博客文章的 slug 和页面内容在生成的每个页面中都可用: +7. 添加以下代码到[查询集合](/zh-cn/guides/content-collections/#查询集合)中,以使每篇博客文章的 slug 和页面内容在生成的每个页面中都可用: ```astro title="src/pages/posts/[...slug].astro" ---