- 起步
- 核心概念
- 教程
- 基础内容
- 内置功能
- 集成
- 操作指南
- 使用指南
- 配置
- 参考
- 社区资源
- 其他
- 为文档做出贡献
- 手动安装 Astro
- 升级到 Astro v4
- 升级到 Astro v3
- 升级到 Astro v2
- Legacy v0.x Upgrade Guide
- 从 Create React App(CRA)迁移
- 关于本教程
- 第一单元:前期准备
- 准备开发环境
- 创建你的第一个 Astro 项目
- 你的第一行 Astro 代码
- 创建在线代码仓库
- 将你的网站部署到网络
- 第二单元:页面
- 创建你的第一个 Astro 页面
- 编写你的第一篇 Markdown 博客文章
- 添加「关于你」的动态内容
- 给你的「关于」页面添加样式
- 应用全局样式
- 第三单元:组件
- 创建一个可重复使用的导航组件
- 创建社交媒体 footer
- 自己动手搭建导航 Header
- 编写你的第一个浏览器脚本
- 第四单元:布局
- 编写你的第一个布局
- 创建并传递数据到博客布局
- 布局结合,两全其美
- 第五单元 - Astro API
- 创建文章存档页
- 生成标签页面
- 编写标签索引页面
- 添加 RSS 订阅源
- 第六单元 - Astro 群岛
- 搭建你的第一个 Astro 岛屿
- 回到干燥的陆地。让你的博客从白天到黑夜,无需岛屿!
- 恭喜你!
- @astrojs/mdx
- 添加阅读时间
- 创建开发者工具栏应用
- @astrojs/alpinejs
- @astrojs/lit
- @astrojs/preact
- @astrojs/react
- @astrojs/solid-js
- @astrojs/svelte
- @astrojs/vue
- @astrojs/cloudflare
- @astrojs/netlify
- @astrojs/node
- @astrojs/vercel
- @astrojs/db
- @astrojs/markdoc
- @astrojs/partytown
- @astrojs/sitemap
- @astrojs/tailwind
- 使用流式处理来提升页面性能
- 从 Gatsby 迁移
- 从 Next.js 迁移
- 从 NuxtJS 迁移
- 从 Docusaurus 迁移到 Astro
- 从 Eleventy 迁移到 Astro
- 从 GitBook 迁移到 Astro
- 从Gridsome迁移到Astro
- 从 Hugo 迁移
- 从 Jekyll 迁移
- 从 Pelican 迁移
- 从 SvelteKit 迁移
- 从VuePress迁移
- 从 WordPress 迁移
- 状态共享
- Storyblok 与 Astro
- Contentful 与 Astro
- ButterCMS & Astro
- Builder.io & Astro
- Cosmic 与 Astro
- DatoCMS 与 Astro
- Front Matter CMS 与 Astro
- Ghost & Astro
- Hashnode & Astro
- Hygraph 与 Astro
- Keystatic & Astro
- Kontent.ai 与 Astro
- 无头模式的 Statamic 与 Astro
- Strapi 与 Astro
- Tina CMS 与 Astro
- Umbraco & Astro
- 无头(headless)模式的 WordPress 与 Astro
- ApostropheCMS 与 Astro
- Caisy & Astro
- CloudCannon与Astro
- Crystallize 与 Astro
- Decap CMS 与 Astro
- Directus 与 Astro
- KeystoneJS 与 Astro
- microCMS & Astro
- Payload CMS 与 Astro
- Prepr CMS & Astro
- Prismic & Astro
- Sanity & Astro
- Sitecore 体验管理器 & Astro
- Spinal & Astro
- 创作内容
- Firebase 与 Astro
- Supabase 与 Astro
- Turso 和 Astro
- Xata 与 Astro
- Appwrite & Astro
- 部署你的 Astro 站点至 Netlify
- 部署你的 Astro 站点至 Vercel
- 如何将你的 Astro 网站部署到 Deno
- 部署你的 Astro 站点至 GitHub Pages
- 部署你的 Astro 站点至 GitLab Pages
- 部署你的 Astro 站点至 Cloudflare Pages
- 将你的 Astro 网站部署到 AWS
- 将你的 Astro 网站通过 Flightcontrol 部署到 AWS
- 将你的 Astro 网站通过 SST 部署到 AWS
- 将你的 Astro 网站部署至 Google Cloud
- 部署你的 Astro 站点至 Google Firebase 托管
- 将你的 Astro 网站部署到 Heroku
- 将你的 Astro 网站部署到 Microsoft Azure
- 部署你的 Astro 站点至 Buddy
- 将你的 Astro 网站部署到 Edgio
- 将你的 Astro 站点部署到 Fly.io
- 将你的 Astro 网站部署到 Render
- 将你的 Astro 网站部署到 Stormkit
- 将你的 Astro 网站部署到 Surge
- 通过 Cleavr 部署你的 Astro 网站
- 将你的 Astro 站点部署到 Kinsta 应用托管
- 将你的 Astro 网站部署到 Space
- 将你的 Astro 站点部署到 Zeabur
- 将你的 Astro 网站部署到 Zerops
- 添加 RSS 摘要
- 安装一个 Vite 或 Rollup 插件
- 构建自定义图像组件
- 使用 API 路由构建表单
- 在 Astro 页面中构建 HTML 表单
- 在 Astro 中使用 Bun
- 调用服务器端点
- 校验验证码
- 用 Docker 来构建你的 Astro 网站
- 动态导入图片
- 为链接添加图标
- 添加 i18n 功能
- 添加最后修改时间
- 在 Astro 组件中共享状态
- 使用 Tailwind Typography 美化渲染后的 Markdown
- Unknown compiler error.
- Astro.redirect is not available in static mode.
- Astro.clientAddress is not available in current adapter.
- Astro.clientAddress cannot be used inside prerendered routes.
- Astro.clientAddress is not available in static mode.
- No static path found for requested path.
- Invalid type returned by Astro page.
- Missing value for client:media directive.
- No matching renderer found.
- No client entrypoint specified in renderer.
- Missing hint on client:only directive.
- Invalid value returned by a getStaticPaths path.
- Invalid entry inside getStaticPath's return value
- Invalid value returned by getStaticPaths.
- getStaticPaths RSS helper is not available anymore.
- Missing params property on getStaticPaths route.
- Invalid value for getStaticPaths route parameter.
- getStaticPaths() function required for dynamic routes.
- Invalid slot name.
- Cannot use Server-side Rendering without an adapter.
- No import found for component.
- Invalid prerender export.
- Invalid component arguments.
- Page number param not found.
- Image missing required "alt" property.
- Error while loading image service.
- Missing image dimensions
- Failed to retrieve remote image dimensions
- Unsupported image format
- Unsupported image conversion
- Prerendered dynamic endpoint has path collision.
- Expected src to be an image.
- Expected image options.
- Cannot set both densities and widths
- Image not found.
- Could not process image metadata.
- Image not found.
- Could not transform image.
- Unable to set response.
- The middleware didn't return a Response.
- The middleware returned something that is not a Response object.
- The endpoint did not return a Response.
- Value assigned to locals is not accepted.
- Astro.response.headers must not be reassigned.
- Can't load the middleware.
- Local images must be imported.
- Astro.glob() used outside of an Astro file.
- Astro.glob() did not match any files.
- A redirect must be given a location with the Location header.
- Invalid dynamic route.
- Could not find Sharp.
- Unknown Vite Error.
- Could not import file.
- Invalid glob pattern.
- Astro couldn't find the correct page to render
- The provided locale does not exist.
- Index page not found.
- You can't use the current function with the current strategy
- Prerendered routes aren't supported when internationalization domains are enabled.
- 启用了手动国际化路由但没有提供中间件。
- Astro can't render the route.
- Unhandled rejection
- i18n Not Enabled
- Route not found.
- Unknown CSS Error
- CSS Syntax Error
- Unknown Markdown Error.
- Failed to parse Markdown frontmatter.
- Invalid frontmatter injection.
- MDX integration missing.
- Unknown configuration error.
- Specified configuration file not found.
- Legacy configuration detected.
- Unknown CLI Error.
- Failed to generate content types.
- Unknown Content Collection Error.
- Content entry frontmatter does not match schema.
- Invalid content entry slug.
- Content Schema should not contain slug.
- Collection does not exist
- Content and data cannot be in same collection.
- Collection contains entries of a different type.
- Data collection entry failed to parse.
- Duplicate content entry slug.
- Actions must be used with server output.
- Unsupported transform in content config.
教程 - 在项目中使用内容集合
内容集合是一种强大的方式来管理类似内容的组合,例如博客文章。集合有助于组织你的文档,验证你的 YAML frontmatter,以及为你所有内容提供自动的 TypeScript 类型安全(即使你自己不编写任何 TypeScript 代码)。
准备好…
- 将你的博客文章文件夹移动到
src/content/
中 - 创建一个模式用于定义你的博客文章 frontmatter
- 使用
getCollection()
获取博客文章内容和元数据
前期准备
你将需要 一个已有的 Astro 项目并在 src/pages/
文件夹中包含 Markdown 或 MDX 文件。
本教程使用构建博客教程的完整项目来演示如何将博客转换为内容集合。你可以在本地 fork 并使用该代码库,或者通过在 StackBlitz 上编辑博客教程代码在浏览器中完成教程。
你也可以使用自己的 Astro 项目按照这些步骤进行操作,但你需要根据你的代码库调整指令。
我们建议你先使用我们的示例项目完成这个简短的教程。然后,你可以利用所学知识在自己的项目中创建内容集合。
构建博客教程代码
在构建博客入门教程中,你了解了 Astro 内置的基于文件的路由:src/pages/
文件夹中的任何 .astro
、.md
或 .mdx
文件都会自动成为你站点上的页面。
为了创建位于 https://example.com/posts/post-1/
的第一篇博客文章,你已经创建了一个 /posts/
文件夹,并添加了文件 post-1.md
。每当你想在站点上添加一篇新的博客文章时,你只需在该文件夹中添加一个新的 Markdown 文件。
页面与集合
即使使用内容集合,你仍会使用 src/pages/
文件夹来管理独立页面,例如“关于我”页面。但是,将博客文章移动到特殊的 src/content/
文件夹中将允许你使用更强大和高效的 API 来生成博客文章索引和展示单独的博客文章。
与此同时,你将在代码编辑器中获得更好的提示和自动补全,因为你将拥有一个 模式(schema),用于为每篇文章定义一个通用的结构,Astro 也将帮助你强制匹配该结构。在模式中,你可以指定什么时候 frontmatter 属性是需要的,例如描述或作者信息,以及每个属性的数据类型应该是什么,例如字符串或数组。这将更早地发现许多错误,并提供了详细的错误信息,帮助你快速定位问题。
在我们的指南中阅读更多关于Astro 内容集合(content collections)的内容,或者按照以下说明开始将基本博客从 src/pages/posts/
转换到 src/content/posts/
。
测试一下
哪个类型的页面你可能会保留在
src/pages/
中?哪一个不是将博客文章移动到内容集合的好处?
内容集合使用了 TypeScript……
使用内容集合扩展博客教程
下面的步骤将向你展示如何通过为博客文章创建内容集合来扩展构建博客教程的完整项目。
升级依赖项
在终端中运行以下命令,将 Astro 升级到最新版本,并将所有集成升级为它们的最新版本:
- npm
- pnpm
- Yarn
# 更新至 Astro v4.x npm install astro@latest # 示例:更新博客教程的 Preact 集成 npm install @astrojs/preact@latest
# 更新至 Astro v4.x pnpm add astro@latest # 示例:更新博客教程的 Preact 集成 pnpm add @astrojs/preact@latest
# 更新至 Astro v4.x yarn add astro@latest # 示例:更新博客教程的 Preact 集成 yarn add @astrojs/preact@latest
在博客教程中,使用的是不那么严格的
base
TypeScript 设置。要使用内容集合,你必须通过以下方式之一为内容集合设置 TypeScript,即使用strict
或strictest
设置,或者在tsconfig.json
中添加两个选项。为了在博客教程示例的其余部分中使用内容集合,将以下两个 TypeScript 配置选项添加到配置文件中:
tsconfig.json{ // 注意:如果你使用的是 "astro/tsconfigs/strict" 或 "astro/tsconfigs/strictest",则无需更改 "extends": "astro/tsconfigs/base", "compilerOptions": { "strictNullChecks": true, "allowJs": true } }
为博客文章创建一个集合
创建一个新的名为
src/content/posts/
的集合(文件夹)。将所有现有的博客文章(
.md
文件)从src/pages/posts/
移动到这个新的集合中。创建一个
src/content/config.tssrc/content/config.ts
文件,用于为你的postsCollection
定义一个模式。对于现有的博客教程代码,向文件中添加如下内容以定义其博客文章中使用的所有 frontmatter 属性:// 从 `astro:content` 导入辅助工具 import { z, defineCollection } from "astro:content"; // 为每一个集合定义一个 `type` 和 `schema` 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()) }) }); // 导出一个单独的 `collections` 对象来注册你的集合 export const collections = { posts: postsCollection, };
为了让 Astro 识别你的 schema,退出开发服务器(
CTRL + C
)并运行以下命令:npx astro sync
。这将为内容集合 API 定义astro:content
模块。重新启动开发服务器以继续教程。
从一个集合生成页面
生成一个名为
src/pages/posts/[...slug].astro
的页面文件。当 Markdown 和 MDX 文件位于一个集合内时,Astro 的基于文件的路由不再自动将它们转换为页面,因此你需要创建一个页面来负责生成每个单独的博客文章。添加以下代码来查询你的集合,以便将每个博客文章的 slug 和页面内容提供给它将生成的每个页面:
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(); ---
在 Markdown 页面的布局中渲染你的
src/pages/posts/[...slug].astro<Content />
组件,这样可以为所有的文章指定一个共同的布局。--- 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(); ---<MarkdownPostLayout frontmatter={entry.data}> <Content /></MarkdownPostLayout>
移除每个单独的博客文章 frontmatter 中的
src/content/posts/post-1.mdlayout
定义。当渲染时,你的内容会被包裹在一个布局中,因此不再需要这个属性。---layout: ../../layouts/MarkdownPostLayout.astro title: 'My First Blog Post' pubDate: 2022-07-01 ... ---
用 getCollection()
代替 Astro.glob()
在任何你有博客文章列表的地方,比如教程中的博客页面 (
src/pages/blog.astrosrc/pages/blog.astro/
),你需要将Astro.glob()
替换为getCollection()
来获取 Markdown 文件的内容和元数据。---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"); ---
你还需要更新对于每个
src/pages/blog.astropost
所返回数据的引用。现在你会在每个对象的data
属性上找到你的 frontmatter 值。此外,当使用集合时,每个post
对象将具有一个页面的slug
,而不是完整的 URL。--- 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"); --- <BaseLayout pageTitle={pageTitle}> <p>This is where I will post about my journey learning Astro.</p> <ul> { allPosts.map((post) => ( <BlogPost url={post.url} title={post.frontmatter.title} />)} <BlogPost url={`/posts/${post.slug}/`} title={post.data.title} /> )) } </ul> </BaseLayout>
教程中的博客项目还会使用
src/pages/tags/[tag].astro
动态为每个标签生成一个页面,并在src/pages/tags/index.astro
中展示标签列表。将与上述相同的修改应用到这两个文件中:
- 使用
getCollection("posts")
代替Astro.glob()
来获取所有博客文章的数据 - 使用
data
而不是frontmatter
来访问所有 frontmatter 值 - 通过为博客文章的
slug
添加/posts/
路径来创建页面 URL
生成单独标签页面的页面现在如下所示:
src/pages/tags/[tag].astro---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; --- <BaseLayout pageTitle={tag}> <p>Posts tagged with {tag}</p> <ul> { posts.map((post) => <BlogPost url={`/posts/${post.slug}/`} title={post.data.title} />) } </ul> </BaseLayout>
试一试 - 在标签索引页面中更新查询语句
导入并使用
getCollection
来获取在src/pages/tags/index.astro
中的博客文章中使用的标签,按照上述相同的步骤进行操作。参考代码
src/pages/tags/index.astro---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 值以匹配你的模式
如果需要的话,更新项目中任何不匹配集合模式的 frontmatter 值,例如在布局中的值。
在博客教程示例中,
pubDate
是一个字符串。而根据定义了博客文章 frontmatter 类型的模式,pubDate
现在将是一个Date
对象。要在博客文章布局中呈现日期,将其转换为字符串:
src/layouts/MarkdownPostLayout.astro... <BaseLayout pageTitle={frontmatter.title}> <p>{frontmatter.pubDate.toString().slice(0,10)}</p> <p><em>{frontmatter.description}</em></p> <p>Written by: {frontmatter.author}</p> <img src={frontmatter.image.url} width="300" alt={frontmatter.image.alt} /> ...
更新 RSS 函数
最后,博客教程项目包含了一个 RSS feed。这个函数也必须使用
src/pages/rss.xml.jsgetCollection()
来返回你的博客文章信息。然后,你将使用返回的data
对象来生成 RSS 项。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: `<language>en-us</language>`, }) }
要查看使用内容集合的博客教程的完整示例,请参阅教程存储库的 Content Collections 分支。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论