添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
冷静的勺子  ·  中间件 | Docs·  1 周前    · 
健壮的香烟  ·  Deploy code to Astro ...·  1 月前    · 
不要命的野马  ·  Content Collections | ...·  1 月前    · 
热心的烈马  ·  TypeScript | Docs·  2 月前    · 
果断的跑步鞋  ·  内容集合 | Docs·  2 月前    · 
高大的脸盆  ·  Java 中的字符串·  4 周前    · 
读研的豌豆  ·  常見問題 - HA Go·  2 月前    · 

在任何 Astro 项目中, 内容集合 是使用 Markdown 和 MDX 的最佳方式。内容集合帮助管理你的文档,校验 frontmatter,并为所有内容提供自动 TypeScript 类型安全。

什么是内容集合?

段落标题 什么是内容集合?

一个 内容集合 是保留在 src/content 目录中的任何顶级目录,比如 src/content/newsletter src/content/authors 。内容集合只允许在 src/content 目录中。此目录不能用于其他用处。

一个 内容条目 是存储在内容集合目录中的任何内容片段。内容条目存储在 Markdown ( .md ) 或 MDX ( .mdx ) 文件中。你可以使用任何想用的文件名,但是我们建议使用一致的命名方案 (小写,破折号而不是空格) ,以便更容易地查找和组织内容。条目可以使用内容文件的格式,包括 Markdown ( .md ) 和 MDX ( .mdx 使用 MDX 集成 ) 或者被支持的二者之一的数据格式:YAML ( .yaml ) 和 JSON ( .json )。我们建议为文件使用一致的命名方案(小写、破折号而不是空格),以便更轻松地查找和组织你的内容,但这不是必须的。你还可以通过在文件名前添加下划线 (_) 来 从构建中排除条目

  • 文件夹 src/content/
    • 文件夹 newsletter/ “newsletter” 集合
      • week-1.md 一个集合条目
      • week-2.md 一个集合条目
      • week-3.md 一个集合条目

一旦有了一个集合,你就可以使用 Astro 的内置内容 API 开始 查询集合

Astro 将内容集合的重要元数据存储在项目中的 .astro 目录。你不需要采取任何措施来维护或更新此目录。我们希望你在项目中完全忽略它。

只要运行 astro dev astro build 命令, .astro 目录就会自动更新。你可以随时运行 astro sync 来手动更新 .astro 目录。

如果两个文件表示不同类型的内容(例如,一篇博客文章和一个作者描述文件),那么它们很可能应属于不同的集合。这一点非常重要,因为许多特性(frontmatter 验证、自动 TypeScript 类型安全)要求集合中的所有条目共享相似的结构。

如果你发现自己正在处理不同类型的内容,则应该创建多个集合来表示每种类型。你可以在项目中创建任意多个不同的集合。

  • 文件夹 src/content/
    • 文件夹 newsletter/
      • week-1.md
      • week-2.md
    • 文件夹 blog/
      • post-1.md
      • post-2.md
    • 文件夹 authors/
      • grace-hopper.json
      • alan-turing.json

使用子目录进行组织

段落标题 使用子目录进行组织

内容集合始终是 src/content/ 目录中的顶级文件夹。你不能将一个集合嵌套在另一个集合中。不过,你可以使用子目录来组织集合中的内容。

例如,可以使用以下目录结构在单个 docs 集合中组织 i18n 翻译。当查询这个集合时,你将能够使用文件路径按语言过滤结果。

  • 文件夹 src/content/
    • 文件夹 docs/ 此集合使用子目录按语言进行组织
      • 文件夹 en/
      • 文件夹 es/
      • 文件夹 de/

要充分利用内容集合,请在项目中创建一个 src/content/config.ts 文件(也支持 .js .mjs )。这是一个特殊的文件,Astro 将自动加载并使用它来配置你的内容集合。

src/content/config.ts
// 1. 从 `astro:content` 导入
import { defineCollection } from 'astro:content';
// 2. 定义集合
const blogCollection = defineCollection({ /* ... */ });
// 3. 导出一个 `collections` 对象来注册集合
// 这个键应该与 `src/content` 中的集合目录名匹配
export const collections = {
'blog': blogCollection,
};

如果你 没有 已经在 tsconfig.json 文件中扩展 Astro 推荐的 strict strictest 的 TypeScript 设置,你可能需要更新 tsconfig.json 来启用 strictNullChecks

tsconfig.json
{
// 注意:如果使用 `astro/tsconfigs/strict` 或 `astro/tsconfigs/strictest`,则不需要更改
"extends": "astro/tsconfigs/base",
"compilerOptions": {
"strictNullChecks": true
}
}

如果在 Astro 项目中使用 .js 或者 .mjs 文件,你可以启用 IntelliSense,并在编辑器中通过在 tsconfig.json 中启用 allowJs 来进行类型检查:

tsconfig.json
{
// 注意:如果使用 `astro/tsconfigs/strict` 或 `astro/tsconfigs/strictest`,则不需要更改
"extends": "astro/tsconfigs/base",
"compilerOptions": {
"strictNullChecks": true,
"allowJs": true
}
}

schema 在集合中强制执行一致的 frontmatter。schema 保证 当你需要引用或查询它时,你的 frontmatter 以可预测的形式存在。如果任何文件违反了它的集合 schema,Astro 将提供一个有用的错误让你知道。

schema 还使 Astro 能为内容自动生成 TypeScript 类型。为集合定义 schema 时,Astro 将自动生成并向其应用 TypeScript 接口。查询集合时,结果是完全支持 TypeScript,包括属性自动完成和类型检查。

要定义你的第一个内容 schema,如果该文件不存在的话,新建一个 src/content/config.ts 文件( .js .mjs 后缀也是支持的)。此文件应该:

  • astro:content 导入适当的工具
  • 定义要验证的每个集合 。这包括一个 type (Astro v2.5.0 中引入) 指定集合是否包含像 Markdown ( type: 'content' ) 的内容创作格式或是像 JSON 及 YAML ( type: 'data' ) 的数据格式。同样包含一个用来定义 frontmatter 形式或条目数据的 schema
  • 导出一个 collections 对象 来注册你的集合。
  • src/content/config.ts
    // 1. 从 `astro:content` 导入适当的工具。
    import { z, defineCollection } from 'astro:content';
    // 2. 定义要用 schema 验证的每个集合。
    const blogCollection = defineCollection({
    type: 'content', // v2.5.0 及之后
    schema: z.object({
    title: z.string(),
    tags: z.array(z.string()),
    image: z.string().optional(),
    }),
    });
    // 3. 导出一个 `collections` 对象来注册你的集合。
    export const collections = {
    'blog': blogCollection,
    };

    你可以多次使用 defineCollection() 创建多个 schema。所有集合必须从这个 collections 对象中导出。

    src/content/config.ts
    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,
    };

    随着项目的增长,你还可以自由地重新组织代码库,并将逻辑移出 src/content/config.ts 文件。分别定义 schema 对于跨多个集合重用 schema 和与项目的其他部分共享 schema 非常有用。

    src/content/config.ts
    // 1. 导入你的实用工具和 schema
    import { defineCollection } from 'astro:content';
    import { blogSchema, authorSchema } from '../schemas';
    // 2. 定义你的集合
    const blogCollection = defineCollection({
    type: 'content',
    schema: blogSchema,
    });
    const authorCollection = defineCollection({
    type: 'data',
    schema: authorSchema,
    });
    // 3. 导出多个集合以注册它们
    export const collections = {
    'blog': blogCollection,
    'authors': authorCollection,
    };

    使用第三方集合 schema

    段落标题 使用第三方集合 schema

    你可以从任何地方导入集合 schema,包括外部 npm 包。这在使用提供了它们自己的集合 schema 的主题和库时非常有用。

    src/content/config.ts
    import { blogSchema } from 'my-blog-theme';
    const blogCollection = defineCollection({ type: 'content', schema: blogSchema });
    // 导出从 'my-blog-theme' 导入的 schema 为 `blog` 集合
    export const collections = {
    'blog': blogCollection,
    };

    Astro 使用 Zod 驱动其内容 schema。Astro 使用 Zod 能够验证集合中每个文件的 frontmatter,并在从项目内部查询内容时提供自动的 TypeScript 类型。

    要在 Astro 中使用 Zod,请从 "astro:content" 中导入 z 。这是 Zod 库的重新导出,它支持 Zod 的所有特性。查看 Zod 的 README 获得关于 Zod 如何工作以及可用特性的完整文档。

    // 例子:许多常见 Zod 数据类型的备忘单
    import { z, defineCollection } from 'astro:content';
    defineCollection({
    schema: z.object({
    isDraft: z.boolean(),
    title: z.string(),
    sortOrder: z.number(),
    image: z.object({
    src: z.string(),
    alt: z.string(),
    }),
    author: z.string().default('Anonymous'),
    language: z.enum(['en', 'es']),
    tags: z.array(z.string()),
    // 一个可选的 frontmatter 属性。非常普遍!
    footnote: z.string().optional(),
    // 在 frontmatter 中,周围没有引号的日期被解释为 Date 对象
    publishDate: z.date(),
    // 可以将日期字符串 (例如 "2022-07-08") 转换为 Date 对象
    // publishDate: z.string().transform((str) => new Date(str)),
    //Advanced: 验证字符串是否也是电子邮件
    authorContact: z.string().email(),
    // Advanced: 验证该字符串也是一个 URL
    canonicalURL: z.string().url(),
    })
    })

    集合条目也可以”引用”其他相关条目。

    使用集合 API 中的 reference() 函数,可以将集合 schema 中的属性定义为另一个集合中的条目。例如,你可以要求每个 space-shuttle 条目都包含一个 pilot 属性,该属性使用 pilot 集合自己的 schema 进行类型检查、自动填充和验证。

    一个常见的示例是引用以 JSON 形式存储的可重用作者个人资料或存储在同一集合中的相关帖子 URL 的博客文章:

    import { defineCollection, reference, z } from 'astro:content';
    const blog = defineCollection({
    type: 'content',
    schema: z.object({
    title: z.string(),
    // 通过 `id` 引用 `authors` 集合中的单个作者
    author: reference('authors'),
    // 通过 `slug` 引用 `blog` 集合中的相关帖子数组
    relatedPosts: z.array(reference('blog')),
    })
    });
    const authors = defineCollection({
    type: 'data',
    schema: z.object({
    name: z.string(),
    portfolio: z.string().url(),
    })
    });
    export const collections = { blog, authors };

    此示例博客文章指定相关帖子的 slug 和帖子作者的 id

    src/content/blog/welcome.md
    ---
    title: "Welcome to my blog"
    author: ben-holmes # 引用 `src/content/authors/ben-holmes.json`
    relatedPosts:
    - about-me # 引用 `src/content/blog/about-me.md`
    - my-year-in-review # 引用 `src/content/blog/my-year-in-review.md`
    ---

    当使用 type: 'content' 时,每个内容条目从它的 文件 id 生成一个 URL 友好的 slug 属性。slug 用于直接从集合中查询条目。在根据内容创建新页面和 URL 时,它也很有用。

    你可以通过将自己的 slug 属性添加到文件 frontmatter 来覆盖条目生成的 slug。这类似于其他 Web 框架的 “permalink” 特性。 "slug" 是一个特殊的保留属性名称,不允许出现在自定义集合 schema 中,也不会出现在条目的 data 属性中。

    ---
    title: 我的博客文章
    slug: my-custom-slug/supports/slashes
    ---
    你的博客文章内容在这里。

    Astro 提供了两个函数来查询一个集合并返回一个(或多个)内容条目: getCollection() getEntry()

    import { getCollection, getEntry } from 'astro:content';
    // 获取集合中的所有条目。
    // 需要集合的名称作为参数。
    // 示例:检索 `src/content/blog/**`
    const allBlogPosts = await getCollection('blog');
    // 从集合中获取单个条目。
    // 需要集合的名称以及
    // 条目 `slug`(内容集合)或 `id`(数据集合)
    // 示例:检索 `src/content/authors/grace-hopper.json`
    const graceHopperProfile = await getEntry('authors', 'grace-hopper');

    这两个函数都返回由 CollectionEntry 类型定义的内容条目。

    首次查询集合条目后,必须再单独查询 schema 中定义的引用 。你可以再次使用 getEntry() 函数或 getEntries() 从返回的 data 对象中检索引用的条目。

    src/pages/blog/welcome.astro
    ---
    import { getEntry, getEntries } from 'astro:content';
    const blogPost = await getEntry('blog', 'welcome');
    // 检索单一引用
    const author = await getEntry(blogPost.data.author);
    // 检索引用数组
    const relatedPosts = await getEntries(blogPost.data.relatedPosts);
    ---
    <h1>{blogPost.data.title}</h1>
    <p>作者:{author.data.name}</p>
    <!-- ... -->
    <h2>你可能也喜欢:</h2>
    {relatedPosts.map(p => (
    <a href={p.slug}>{p.data.title}</a>
    ))}

    getCollection() 接受一个可选的 “filter” 回调,它允许你基于条目的 id slug data (frontmatter) 属性对查询进行过滤。对于 type: content 的集合,你也可以通过 slug 来过滤。

    你可以使用它根据你喜欢的任何内容条件进行筛选。例如,你可以通过前端属性(如 draft )进行过滤,以防止任何博客文章草稿发布到你的博客上:

    // 例子:使用 `draft: true` 过滤内容条目
    import { getCollection } from 'astro:content';
    const draftBlogEntries = await getCollection('blog', ({ data }) => {
    return data.draft !== true;
    });

    你还可以创建草稿页面,这些页面在运行开发服务器时可用,但在生产中不会构建:

    // 示例:仅当构建生产时,才过滤掉具有 `draft: true` 的内容条目
    import { getCollection } from 'astro:content';
    const blogEntries = await getCollection('blog', ({ data }) => {
    return import.meta.env.PROD ? data.draft !== true : true;
    });

    filter 参数还支持按集合中的嵌套目录进行筛选。由于 id 包含完整的嵌套路径,因此可以根据每个 id 的开头进行筛选,只返回特定嵌套目录中的项目:

    // 例子:按集合中的子目录筛选条目
    import { getCollection } from 'astro:content';
    const englishDocsEntries = await getCollection('docs', ({ id }) => {
    return id.startsWith('en/');
    });

    使用 Astro 模板中的内容

    段落标题 使用 Astro 模板中的内容

    查询完集合条目后,可以直接在 Astro 组件模板内访问每个条目。这使你可以呈现诸如内容链接(使用内容 slug )或关于内容的信息(使用 data 属性)之类的 HTML。

    有关将内容呈现为 HTML 的信息,请参见下面的 将内容渲染成 HTML

    src/pages/index.astro
    ---
    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>

    将内容作为属性传递

    段落标题 将内容作为属性传递

    组件还可以将整个内容条目作为属性传递。

    如果这样做,你可以使用 CollectionEntry 实用工具使用 TypeScript 正确地输入组件属性。此实用工具接受与集合 schema 名称匹配的字符串参数,并将继承该集合 schema 的所有属性。

    src/components/BlogCard.astro
    ---
    import type { CollectionEntry } from 'astro:content';
    interface Props {
    post: CollectionEntry<'blog'>;
    }
    // `post` 将匹配 `blog` 集合 schema 类型
    const { post } = Astro.props;
    ---

    一旦查询,你可以使用 render() 函数属性将集合条目渲染为 HTML。调用此函数可以访问渲染的内容和元数据,包括 <Content/> 组件和所有呈现标题的列表。

    src/pages/render-example.astro
    ---
    import { getEntry } from 'astro:content';
    const entry = await getEntry('blog', 'post-1');
    const { Content, headings } = await entry.render();
    ---
    <p>发布于:{entry.data.published.toDateString()}</p>
    <Content />

    内容集合存储在 src/pages/ 目录之外。这意味着默认情况下不为集合项生成路由。你将需要手动创建一个新的 动态路由 ,以便从集合条目生成 HTML 页面。动态路由将映射传入的请求参数(例如:在 src/pages/blog/[...slug].astro 中的 Astro.params.slug )获取集合中的正确条目。

    生成路由的确切方法将取决于你的构建 output 模式: static (默认值) 或 server (针对 SSR)。

    构建为静态输出(默认)

    段落标题 构建为静态输出(默认)

    如果你正在构建一个静态网站(Astro 的默认行为),你可以使用 getStaticPaths() 函数在构建过程中从一个 src/pages/ 组件创建多个页面。

    getStaticPaths() 中调用 getCollection() 查询你的内容或数据集合 。然后,使用每个内容条目的 slug 属性(内容集合)或 id 属性(数据集合)创建你的新 URL 路径。

    src/pages/posts/[...slug].astro
    ---
    import { getCollection } from 'astro:content';
    // 1. 为每个集合条目生成一个新路径
    export async function getStaticPaths() {
    const blogEntries = await getCollection('blog');
    return blogEntries.map(entry => ({
    params: { slug: entry.slug }, props: { entry },
    }));
    }
    // 2. 当渲染的时候,你可以直接从属性中得到条目
    const { entry } = Astro.props;
    const { Content } = await entry.render();
    ---
    <h1>{entry.data.title}</h1>
    <Content />

    这将为 blog 集合中的每个条目生成一个新页面。例如, src/content/blog/hello-world.md 上的条目将有一个 hello-world 的 slug,因此它的最终 URL 将是 post/hello-world/

    构建为服务器输出(SSR)

    段落标题 构建为服务器输出(SSR)

    如果你正在构建一个动态网站(使用 Astro 的 SSR 支持),则不需要在构建期间提前生成任何路径。相反,你的页面应该检查请求(使用 Astro.request Astro.params )以按需找到 slug ,然后使用 getEntry() 获取它。

    src/pages/posts/[...slug].astro
    ---
    import { getEntry } from 'astro:content';
    // 1. 从传入的服务器请求中获取 slug
    const { slug } = Astro.params;
    if (slug === undefined) {
    throw new Error("Slug is required");
    }
    // 2. 直接使用请求 slug 查询条目
    const entry = await getEntry('blog', slug);
    // 3. 如果条目不存在,则重定向到 404
    if (entry === undefined) {
    return Astro.redirect("/404");
    }
    // 4. (可选)在模板中将条目呈现为 HTML
    const { Content } = await entry.render();
    ---

    从基于文件的路由迁移

    段落标题 从基于文件的路由迁移

    如果你已经有了一个现成的 Astro 项目,比如一个博客,它使用 Markdown 或 MDX 文件在 src/pages/ 内部的子文件夹中,可以考虑将相关内容或数据文件迁移到内容集合中。

    请参考我们的 手把手教程 中的示例,了解如何将位于 src/pages/posts/ 的基本博客示例转换为 src/content/posts 目录下的示例。该教程使用了 构建博客教程的完整项目 的代码库。

    [email protected]

    如果你正在处理 data 的类型集合,Astro 将为你的编辑器生成 JSON schema 文件,以获取智能提示和类型检查。这使用了一个名为 zod-to-json-schema 的库,基于你在 src/content/config.ts 中定义的集合生成项目中每个数据集合的单独文件。

    此功能要求你手动将 schema 的文件路径设置为集合中每个数据条目文件的 $schema 值:

    src/content/authors/armand.json
    {
    "$schema": "../../../.astro/collections/authors.schema.json",
    "name": "Armand",
    "skills": ["Astro", "Starlight"]
    }

    或者,你可以在编辑器设置中设置这个值。例如,要在 VSCode 的 json.schemas 设置 中设置这个值,请提供要匹配的文件路径和你的 JSON schema 的位置:

    {
    "json.schemas": [
    {
    "fileMatch": [
    "/src/content/authors/**"
    ],
    "url": "./.astro/collections/authors.schema.json"
    }
    ]
    }
    [email protected]

    如果你正在处理大量的集合,你可能希望通过 experimental.contentCollectionCache 标志启用缓存的构建。这个实验性的功能优化了 Astro 的构建过程,使得未改变的集合能够在构建之间被存储和复用。

    在许多情况下,这可以导致显著的构建性能提升。

    虽然这个功能正在稳定中,但你也可能会遇到与存储缓存有关的问题。所以你始终可以通过运行以下命令重置你的构建缓存:

    npm run astro build -- --force

    用 Remark 修改 Frontmatter

    段落标题 用 Remark 修改 Frontmatter

    Astro 支持 remark 或者 rehype 插件 直接修改 frontmatter 。你可以通过使用从 render() 返回的 remarkPluginFrontmatter 属性在一个内容条目中访问这个修改过的 Frontmatter:

    ---
    import { getEntry } from 'astro:content';
    const blogPost = await getEntry('blog', 'post-1');
    const { remarkPluginFrontmatter } = await blogPost.render();
    ---
    <p>{blogPost.data.title}{remarkPluginFrontmatter.readingTime}</p>
    相关操作指南: 添加阅读时间

    Remark 和 rehype pipeline 只在渲染内容时运行,这就解释了为什么只有在对内容条目调用 render() 之后才可以使用 remarkPluginFrontmatter 。相比之下, getCollection() getEntry() 无法直接返回这些值,因为它们不会渲染内容。

    在 frontmatter 中处理日期

    段落标题 在 frontmatter 中处理日期

    内容集合中可以使用多种日期格式,但你的集合 schema 必须匹配你在 Markdown 或 MDX YAML 中 frontmatter 所使用的格式。

    YAML 使用 ISO-8601 标准来表示日期,其以 yyyy-mm-dd 格式(例如 2021-07-28 )表示并配合 z.date() 的 schema 类型来使用:

    src/pages/posts/example-post.md
    ---
    title: 我的博客文章
    pubDate: 2021-07-08
    ---

    如果没有提供时区,日期格式将指定为 UTC 格式。如果你需要指定一个时区,你可以使用 ISO 8601 格式。

    src/pages/posts/example-post.md
    ---
    title: 我的博客文章
    pubDate: 2021-07-08T12:00:00-04:00
    ---

    想要仅渲染完整 UTC 时间戳中的 YYYY-MM-DD ,可以使用 JavaScript 的 slice 方法来移除时间戳:

    src/layouts/ExampleLayout.astro
    ---
    const { frontmatter } = Astro.props;
    ---
    <h1>{frontmatter.title}</h1>
    <p>{frontmatter.pubDate.toISOString().slice(0,10)}</p>

    要查看使用 toLocaleDateString 格式化日期、月份和年份的例子,请查看官方 Astro 博客模板中的 <FormattedDate /> 组件