- 起步
- 核心概念
- 教程
- 基础内容
- 内置功能
- 集成
- 操作指南
- 使用指南
- 配置
- 参考
- 社区资源
- 其他
- 为文档做出贡献
- 手动安装 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.
Contentful 与 Astro
Contentful 是一个无头(headless) CMS,允许你管理内容,集成其他服务并发布到多个平台。
与 Astro 集成
在本节中,我们将使用 Contentful SDK 来连接你的 Contentful 空间与 Astro,实现零客户端 JavaScript。
前提条件
首先,你需要以下内容:
一个 Astro 项目 - 如果你还没有 Astro 项目,我们的安装指南会帮助你迅速上手。
Contentful 账号和 Contentful 空间。如果你还没有账号,可以注册一个免费账号并创建一个新的 Contentful 空间。如果你已经有一个空间,也可以使用现有空间。
Contentful 凭证 - 你可以在 Contentful 仪表板中的设置 > API 密钥中找到以下凭证。如果你还没有 API 密钥,请选择添加 API 密钥。
- Contentful space ID - 你的Contentful 空间 的 ID。
- Contentful delivery access token - 用于从你的 Contentful 空间获取已发布内容的访问令牌。
- Contentful preview access token - 用于从你的 Contentful 空间获取未发布内容的访问令牌。
设置凭证
要将你的 Contentful 空间凭证添加到 Astro 中,在项目根目录中创建一个名为.env
的文件,并添加以下变量:
CONTENTFUL_SPACE_ID=YOUR_SPACE_ID
CONTENTFUL_DELIVERY_TOKEN=YOUR_DELIVERY_TOKEN
CONTENTFUL_PREVIEW_TOKEN=YOUR_PREVIEW_TOKEN
现在,你可以在项目中使用这些环境变量。
如果你希望为 Contentful 环境变量启用智能感知,你可以在src/
目录中创建一个名为env.d.ts
的文件,并像这样配置ImportMetaEnv
:
interface ImportMetaEnv {
readonly CONTENTFUL_SPACE_ID: string;
readonly CONTENTFUL_DELIVERY_TOKEN: string;
readonly CONTENTFUL_PREVIEW_TOKEN: string;
}
你的根目录现在应该包含这些新文件:
文件夹src/
- env.d.ts
- .env
- astro.config.mjs
- package.json
安装依赖项
要连接到你的 Contentful 空间,请使用下面的命令使用你首选的包管理器同时安装以下两个包:
contentful.js
,官方 Contentful JavaScript SDKrich-text-html-renderer
,用于将 Contentful 的富文本字段渲染为 HTML 的包。
- npm
- pnpm
- Yarn
npm install contentful @contentful/rich-text-html-renderer
pnpm add contentful @contentful/rich-text-html-renderer
yarn add contentful @contentful/rich-text-html-renderer
接下来,在你的项目的 src/lib/
目录中创建一个名为 contentful.ts
的新文件。
import contentful from "contentful";
export const contentfulClient = contentful.createClient({
space: import.meta.env.CONTENTFUL_SPACE_ID,
accessToken: import.meta.env.DEV
? import.meta.env.CONTENTFUL_PREVIEW_TOKEN
: import.meta.env.CONTENTFUL_DELIVERY_TOKEN,
host: import.meta.env.DEV ? "preview.contentful.com" : "cdn.contentful.com",
});
上面的代码片段创建了一个新的 Contentful 客户端,将.env
文件中的凭证传递进去。
最后,你的根目录现在应该包含这些新文件:
文件夹src/
- env.d.ts
文件夹lib/
- contentful.ts
- .env
- astro.config.mjs
- package.json
从 Contentful 获取数据
Astro 组件可以通过使用 contentfulClient
并指定 content_type
从你的 Contentful 账号中获取数据。
例如,如果你有一个名为 “blogPost” 的内容类型,其中包含一个用于标题的文本字段和一个用于内容的富文本字段,你的组件可能如下所示:
---
import { contentfulClient } from "../lib/contentful";
import { documentToHtmlString } from "@contentful/rich-text-html-renderer";
import type { EntryFieldTypes } from "contentful";
interface BlogPost {
contentTypeId: "blogPost",
fields: {
title: EntryFieldTypes.Text
content: EntryFieldTypes.RichText,
}
}
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
---
<body>
{entries.items.map((item) => (
<section>
<h2>{item.fields.title}</h2>
<article set:html={documentToHtmlString(item.fields.content)}></article>
</section>
))}
</body>
你可以在 Contentful 文档 中找到更多的查询选项。
使用 Astro 和 Contentful 制作博客
通过上述设置,你现在可以创建一个使用 Contentful 作为 CMS 的博客。
先决条件
- 一个 Contentful 空间 - 对于本教程,我们建议从一个空的空间开始。如果你已经有一个内容模型,请随意使用它,但你需要修改我们的代码片段以与你的内容模型匹配。
- 集成了 Contentful SDK 的 Astro 项目 - 有关如何在 Astro 项目中设置 Contentful 的详细信息,请参阅 与 Astro 集成。
设置 Contentful 模型
在你的 Contentful 空间中,在 内容模型 部分,创建一个新的内容模型,并设置以下字段和值:
- Name: 博客文章
- API identifier:
blogPost
- Description: 此内容类型用于博客文章
在你新创建的内容类型中,使用添加字段按钮添加5个新字段,具体参数如下:
- 文本字段
- Name: title
- API identifier:
title
(将其他参数保持默认)
- 日期和时间字段
- Name: date
- API identifier:
date
- 文本字段
- Name: slug
- API identifier:
slug
(将其他参数保持默认)
- 文本字段
- Name: description
- API identifier:
description
- 富文本字段
- Name: content
- API identifier:
content
单击保存以保存你的更改。
在你的 Contentful 空间的内容部分,点击添加条目按钮创建一个新条目。然后,填写字段:
- Title:
Astro 真是太棒了!
- Slug:
astro-is-amazing
- Description:
Astro 是一个全新的静态站点生成器,速度快,易于使用。
- Date:
2022-10-05
- Content:
这是我的第一篇博客文章!
点击Publish以保存你的条目。你刚刚创建了你的第一篇博客文章。
随意添加你想要的博客文章,然后切换到你喜欢的代码编辑器,开始使用 Astro 进行开发!
显示博客文章列表
创建一个名为 BlogPost
的新接口,并将其添加到位于 src/lib/
下的 contentful.ts
文件中。此接口将与你在 Contentful 中的博客文章内容类型的字段相匹配。你将使用它来对博客文章条目的响应进行类型定义。
import contentful, { EntryFieldTypes } from "contentful";
export interface BlogPost { contentTypeId: "blogPost", fields: { title: EntryFieldTypes.Text content: EntryFieldTypes.RichText, date: EntryFieldTypes.Date, description: EntryFieldTypes.Text, slug: EntryFieldTypes.Text }}
export const contentfulClient = contentful.createClient({
space: import.meta.env.CONTENTFUL_SPACE_ID,
accessToken: import.meta.env.DEV
? import.meta.env.CONTENTFUL_PREVIEW_TOKEN
: import.meta.env.CONTENTFUL_DELIVERY_TOKEN,
host: import.meta.env.DEV ? "preview.contentful.com" : "cdn.contentful.com",
});
接下来,转到你将从 Contentful 获取数据的 Astro 页面。在本示例中,我们将使用位于 src/pages/
下的主页 index.astro
。
从 src/lib/contentful.ts
中导入 BlogPost
接口和 contentfulClient
。
通过传递 BlogPost
接口来从 Contentful 获取所有带有内容类型为 blogPost
的条目。
---
import { contentfulClient } from "../lib/contentful";
import type { BlogPost } from "../lib/contentful";
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
---
此获取调用将在 entries.items
处返回一个博客文章数组。你可以使用 map()
创建一个新数组 (posts
),以格式化返回的数据。
下面的示例从我们的内容模型中返回 items.fields
属性,以创建博客文章预览,并同时将日期重新格式化为更易读的格式。
---
import { contentfulClient } from "../lib/contentful";
import type { BlogPost } from "../lib/contentful";
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
const posts = entries.items.map((item) => { const { title, date, description, slug } = item.fields; return { title, slug, description, date: new Date(date).toLocaleDateString() };});
---
最后,你可以在模板中使用 posts
来显示每篇博客文章的预览。
---
import { contentfulClient } from "../lib/contentful";
import type { BlogPost } from "../lib/contentful";
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
const posts = entries.items.map((item) => {
const { title, date, description, slug } = item.fields;
return {
title,
slug,
description,
date: new Date(date).toLocaleDateString()
};
});
---<html lang="en"> <head> <title>My Blog</title> </head> <body> <h1>My Blog</h1> <ul> {posts.map((post) => ( <li> <a href={`/posts/${post.slug}/`}> <h2>{post.title}</h2> </a> <time>{post.date}</time> <p>{post.description}</p> </li> ))} </ul> </body></html>
生成单独的博客文章页面
使用与上述相同的方法从 Contentful 获取数据,但这次在将为每篇博客文章创建一个唯一的页面路由。
静态站点生成
如果你使用的是 Astro 的默认静态模式,你将使用 动态路由 和 getStaticPaths()
函数。此函数将在构建时调用,以生成成为页面的路径列表。
在 src/pages/posts/
中创建一个名为 [slug].astro
的新文件。
与 index.astro
上所做的一样,从 src/lib/contentful.ts
导入 BlogPost
接口和 contentfulClient
。
这次,在 getStaticPaths()
函数中获取数据。
---
import { contentfulClient } from "../../lib/contentful";
import type { BlogPost } from "../../lib/contentful";
export async function getStaticPaths() {
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
}
---
然后,将每个条目映射到一个带有 params
和 props
属性的对象。params
属性将用于生成页面的 URL,props
属性将作为属性传递给页面组件。
---
import { contentfulClient } from "../../lib/contentful";import type { BlogPost } from "../../lib/contentful";
export async function getStaticPaths() {
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
const pages = entries.items.map((item) => ({ params: { slug: item.fields.slug }, props: { title: item.fields.title, content: documentToHtmlString(item.fields.content), date: new Date(item.fields.date).toLocaleDateString(), }, })); return pages;}
---
params
内的属性必须与动态路由的名称匹配。由于我们的文件名是 [slug].astro
,因此我们使用了 slug
。
在我们的示例中,props
对象将三个属性传递给页面:
- title(字符串)
- content(将文档转换为 HTML 的富文本文档)
- date(使用
Date
构造函数进行格式化)
最后,你可以使用页面 props
来显示博客文章。
---
import { contentfulClient } from "../../lib/contentful";
import type { BlogPost } from "../../lib/contentful";
export async function getStaticPaths() {
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
const pages = entries.items.map((item) => ({
params: { slug: item.fields.slug },
props: {
title: item.fields.title,
content: documentToHtmlString(item.fields.content),
date: new Date(item.fields.date).toLocaleDateString(),
},
}));
return pages;
}
const { content, title, date } = Astro.props;
---<html lang="en"> <head> <title>{title}</title> </head> <body> <h1>{title}</h1> <time>{date}</time> <article set:html={content} /> </body></html>
在浏览器中导航到 http://localhost:4321/, 然后点击其中一篇文章,以确保你的动态路由正常工作!
服务器端渲染
如果你已经 选择使用 SSR 模式,你将使用一个使用 slug
参数从 Contentful 获取数据的动态路由。
在 src/pages/posts
中创建一个 [slug].astro
页面。使用 Astro.params
来从 URL 中获取 slug,然后将其传递给 getEntries
:
---
import { contentfulClient } from "../../lib/contentful";
import type { BlogPost } from "../../lib/contentful";
const { slug } = Astro.params;
const data = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
"fields.slug": slug,
});
---
如果找不到条目,你可以使用 Astro.redirect
将用户重定向到 404 页面。
---
import { contentfulClient } from "../../lib/contentful";
import type { BlogPost } from "../../lib/contentful";
const { slug } = Astro.params;
try {
const data = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
"fields.slug": slug,
});} catch (error) { return Astro.redirect("/404");
}
---
要将文章数据传递到模板部分,你可以在 try/catch
块外创建一个 post
对象。
使用 documentToHtmlString
将 content
从文档转换为 HTML,并使用 Date 构造函数格式化日期。title
可以保持原样。然后,将这些属性添加到你的 post
对象中。
---
import { contentfulClient } from "../../lib/contentful";
import type { BlogPost } from "../../lib/contentful";
let post;
const { slug } = Astro.params;try {
const data = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
"fields.slug": slug,
});
const { title, date, content } = data.items[0].fields;
post = { title, date: new Date(date).toLocaleDateString(), content: documentToHtmlString(content), };} catch (error) { return Astro.redirect("/404");
}
---
最后,你可以在模板部分引用 post
来显示博客文章。
---
import Layout from "../../layouts/Layout.astro";
import { contentfulClient } from "../../lib/contentful";
import { documentToHtmlString } from "@contentful/rich-text-html-renderer";
import type { BlogPost } from "../../lib/contentful";
let post;
const { slug } = Astro.params;
try {
const data = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
"fields.slug": slug,
});
const { title, date, content } = data.items[0].fields;
post = {
title,
date: new Date(date).toLocaleDateString(),
content: documentToHtmlString(content),
};
} catch (error) {
return Astro.redirect("/404");
}
---<html lang="en"> <head> <title>{post?.title}</title> </head> <body> <h1>{post?.title}</h1> <time>{post?.date}</time> <article set:html={post?.content} /> </body></html>
发布你的网站
要部署你的网站,请访问我们的部署指南,并按照你首选的托管提供商的说明操作。
在 Contentful 更改后重新构建
如果你的项目使用的是 Astro 的默认静态模式,你需要设置一个 Webhook,在内容更改时触发新的构建。如果你的托管提供商是 Netlify 或 Vercel,你可以使用其 Webhook 功能从 Contentful 事件中触发新的构建。
Netlify
要在 Netlify 中设置 Webhook:
转到你的站点仪表板,点击 Build & deploy。
在 Continuous Deployment 选项卡下,找到 Build hooks 部分,然后点击 Add build hook。
为你的 Webhook 提供一个名称,选择要在其上触发构建的分支。点击 Save,然后复制生成的 URL。
Vercel
要在 Vercel 中设置 Webhook:
转到你的项目仪表板,点击 Settings。
在 Git 选项卡下,找到 Deploy Hooks 部分。
为你的 Webhook 提供一个名称,选择要在其上触发构建的分支。点击 Add,然后复制生成的 URL。
将 Webhook 添加到 Contentful
在你的 Contentful 空间的设置中,点击 Webhooks 选项卡,然后通过点击 Add Webhook 按钮创建一个新的 Webhook。为你的 Webhook 提供一个名称,并粘贴你在上一节中复制的 Webhook URL。最后,点击 Save 创建 Webhook。
现在,每当你在 Contentful 中发布新的博客文章时,都会触发新的构建,并更新你的博客。
更多 CMS 指南
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论