Kontent.ai 和 Astro
Kontent.ai 是一个无头 CMS,它允许你以结构化和模块化的方式管理内容,并由 AI 功能提供支持。
与 Astro 集成
标题为“与 Astro 集成”的部分在本节中,你将使用 Kontent.ai TypeScript SDK 将你的 Kontent.ai 项目连接到你的 Astro 应用程序。
先决条件
标题为“先决条件”的部分要开始使用,你需要准备以下内容
-
Kontent.ai 项目 - 如果你还没有 Kontent.ai 帐户,请免费注册并创建一个新项目。
-
Delivery API 密钥 - 你将需要用于已发布内容的环境 ID 和用于获取草稿的预览 API 密钥(可选)。这两个密钥都位于 Kontent.ai 的 Environment Settings -> API keys 选项卡中。
设置凭据
标题为“设置凭据”的部分要将你的 Kontent.ai 凭据添加到 Astro,请在项目根目录中创建一个 .env
文件,并包含以下变量
KONTENT_ENVIRONMENT_ID=YOUR_ENVIRONMENT_IDKONTENT_PREVIEW_API_KEY=YOUR_PREVIEW_API_KEY
现在,这些环境变量就可以在你的 Astro 项目中使用了。
如果你想为这些环境变量提供 TypeScript 智能提示,你可以在 src/
目录中创建一个新的 env.d.ts
文件,并像这样配置 ImportMetaEnv
interface ImportMetaEnv { readonly KONTENT_ENVIRONMENT_ID: string; readonly KONTENT_PREVIEW_API_KEY: string;}
你的根目录现在应包含这些新文件
目录src/
- env.d.ts
- .env
- astro.config.mjs
- package.json
安装依赖
标题为“安装依赖项”的部分要将 Astro 与你的 Kontent.ai 项目连接,请安装 Kontent.ai TypeScript SDK
npm install @kontent-ai/delivery-sdk
pnpm add @kontent-ai/delivery-sdk
yarn add @kontent-ai/delivery-sdk
接下来,在你的 Astro 项目的 src/lib/
目录中创建一个名为 kontent.ts
的新文件。
import { createDeliveryClient } from "@kontent-ai/delivery-sdk";
export const deliveryClient = createDeliveryClient({ environmentId: import.meta.env.KONTENT_ENVIRONMENT_ID, previewApiKey: import.meta.env.KONTENT_PREVIEW_API_KEY,});
阅读更多关于在 Astro 中获取环境变量的信息。
此实现使用 .env
文件中的凭据创建一个新的 DeliveryClient
对象。
previewApiKey
是可选的。使用时,你可以为每个查询配置 Delivery API 端点,以返回内容的最新版本,无论其在工作流中的状态如何。否则,只返回已发布的内容项。
最后,你的 Astro 项目的根目录现在应该包含这些新文件
目录src/
目录lib/
- kontent.ts
- env.d.ts
- .env
- astro.config.mjs
- package.json
获取数据
标题为“获取数据”的部分现在,所有组件都可以使用 DeliveryClient
。要获取内容,请使用 DeliveryClient
和方法链来定义你想要的项目。此示例展示了获取博客文章并将其标题呈现在列表中的基本操作
---import { deliveryClient } from "../lib/kontent";
const blogPosts = await deliveryClient .items() .type("blogPost") .toPromise()---<html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>Astro</title> </head> <body> <ul> {blogPosts.data.items.map(blogPost => ( <li>{blogPost.elements.title.value}</li> ))} </ul> </body></html>
你可以在 Kontent.ai 文档中找到更多查询选项。
使用 Astro 和 Kontent.ai 制作博客
标题为“使用 Astro 和 Kontent.ai 制作博客”的部分通过上述设置,你现在可以创建一个使用 Kontent.ai 作为内容源的博客了。
先决条件
标题为“先决条件”的部分-
Kontent.ai 项目 - 对于本教程,建议使用一个空白项目。如果你的内容模型中已经有一些内容类型,你可以使用它们,但你需要修改代码片段以匹配你的内容模型。
-
已配置从 Kontent.ai 获取内容的 Astro 项目 - 有关如何使用 Kontent.ai 设置 Astro 项目的更多详细信息,请参见上文
设置内容模型
标题为“设置内容模型”的部分在 Kontent.ai 中,导航到 Content model 并使用以下字段和值创建一个新的内容类型
- 名称: Blog Post
- 元素
- 文本字段
- 名称: Title
- 元素必填: 是
- 富文本字段
- 名称: Teaser
- 元素必填: 是
- 此元素中允许: 仅勾选 Text
- 富文本字段
- 名称: Content
- 元素必填: 是
- 日期和时间字段
- 名称: Date
- URL Slug 字段
- 名称: URL slug
- 元素必填: 是
- 自动生成自: 选择“Title”
- 文本字段
然后,点击 Save Changes。
创建内容
标题为“创建内容”的部分现在,导航到 Content & assets 选项卡,并创建一个类型为 Blog Post 的新内容项。使用以下值填充字段
- 内容项名称: Astro
- 标题: Astro is amazing
- 摘要: Astro 是一款一体化框架,可以更快地构建快速网站。
- 内容: 你可以使用 JavaScript 来实现网站功能,但不需要客户端包。
- 日期和时间: 选择今天
- URL slug: astro-is-amazing
完成后,使用顶部的 Publish 按钮发布博客文章。
注意:在进入下一步之前,你可以随意创建任意数量的博客文章。
在 TypeScript 中生成内容模型
标题为“在 TypeScript 中生成内容模型”的部分接下来,你将从你的内容模型中生成 TypeScript 类型。
此步骤是可选的,但它提供了更好的开发体验,并允许你在构建时而不是运行时发现潜在问题。
首先,安装 Kontent.ai JS 模型生成器、ts-node 和 dotenv
npm install @kontent-ai/model-generator ts-node dotenv
pnpm add @kontent-ai/model-generator ts-node dotenv
yarn add @kontent-ai/model-generator ts-node dotenv
然后,将以下脚本添加到 package.json
{ ... "scripts": { ... "regenerate:models": "ts-node --esm ./generate-models.ts" },}
因为类型需要有关你项目的结构信息,而这些信息在公共 API 中不可用,所以你还需要将内容管理 API 密钥添加到 .env
文件中。你可以在 Environment settings -> API keys -> Management API 下生成密钥。
KONTENT_ENVIRONMENT_ID=YOUR_ENVIRONMENT_IDKONTENT_PREVIEW_API_KEY=YOUR_PREVIEW_API_KEYKONTENT_MANAGEMENT_API_KEY=YOUR_MANAGEMENT_API_KEY
最后,添加脚本 generate-models.ts
,该脚本配置模型生成器以生成模型
import { generateModelsAsync, textHelper } from '@kontent-ai/model-generator'import { rmSync, mkdirSync } from 'fs'
import * as dotenv from 'dotenv'dotenv.config()
const runAsync = async () => { rmSync('./src/models', { force: true, recursive: true }) mkdirSync('./src/models')
// change working directory to models process.chdir('./src/models')
await generateModelsAsync({ sdkType: 'delivery', apiKey: process.env.KONTENT_MANAGEMENT_API_KEY ?? '', environmentId: process.env.KONTENT_ENVIRONMENT_ID ?? '', addTimestamp: false, isEnterpriseSubscription: false, })}
// Self-invocation async function;(async () => { await runAsync()})().catch(err => { console.error(err) throw err})
现在,执行它
npm run regenerate:models
pnpm run regenerate:models
yarn run regenerate:models
显示博文列表
标题为“显示博客文章列表”的部分现在你已经准备好获取一些内容了。转到你想要显示所有博客文章列表的 Astro 页面,例如 src/pages
中的主页 index.astro
。
在 Astro 页面的 frontmatter 中获取所有博客文章
---import { deliveryClient } from '../lib/kontent';import type { BlogPost } from '../models';import { contentTypes } from '../models/project/contentTypes';
const blogPosts = await deliveryClient .items<BlogPost> .type(contentTypes.blog_post.codename) .toPromise()---
如果你跳过了模型生成,你也可以使用无类型对象和字符串字面量来定义类型
const blogPosts = await deliveryClient .items() .type("blogPost") .toPromise()
fetch 调用将返回一个 response
对象,其中包含 data.items
中的所有博客文章列表。在 Astro 页面的 HTML 部分,你可以使用 map()
函数来列出博客文章
---import { deliveryClient } from '../lib/kontent';import type { BlogPost } from '../models';import { contentTypes } from '../models/project/contentTypes';
const blogPosts = await deliveryClient .items<BlogPost> .type(contentTypes.blogPost.codename) .toPromise()---<html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>Astro</title> </head> <body> <h1>Blog posts</h1> <ul> {blogPosts.data.items.map(blogPost => ( <li> <a href={`/blog/${blogPost.elements.url_slug.value}/`} title={blogPost.elements.title.value}> {blogPost.elements.title.value} </a> </li> ))} </ul> </body></html>
生成单篇博客文章
标题为“生成单个博客文章”的部分本教程的最后一步是生成详细的博客文章页面。
静态站点生成
标题为“静态站点生成”的部分在本节中,你将使用 Astro 的静态(SSG)模式。
首先,在 /src/pages/blog/
中创建一个文件 [slug].astro
,该文件需要导出一个函数 getStaticPaths
,用于从 CMS 收集所有数据
---import { deliveryClient } from '../../lib/kontent';import type { BlogPost } from '../../models';import { contentTypes } from '../../models/project/contentTypes';
export async function getStaticPaths() { const blogPosts = await deliveryClient .items<BlogPost>() .type(contentTypes.blog_post.codename) .toPromise()---
到目前为止,该函数从 Kontent.ai 获取所有博客文章。代码片段与你在主页上使用的完全相同。
接下来,该函数必须为每个博客文章导出路径和数据。你将文件命名为 [slug].astro
,因此代表 URL slug 的参数称为 slug
---import { deliveryClient } from '../../lib/kontent';import type { BlogPost } from '../../models';import { contentTypes } from '../../models/project/contentTypes';
export async function getStaticPaths() { const blogPosts = await deliveryClient .items<BlogPost>() .type(contentTypes.blog_post.codename) .toPromise()
return blogPosts.data.items.map(blogPost => ({ params: { slug: blogPost.elements.url_slug.value }, props: { blogPost } }))}---
最后一部分是提供 HTML 模板并显示每个博客文章
---import { deliveryClient } from '../../lib/kontent';import type { BlogPost } from '../../models';import { contentTypes } from '../../models/project/contentTypes';
export async function getStaticPaths() { const blogPosts = await deliveryClient .items<BlogPost>() .type(contentTypes.blog_post.codename) .toPromise()
return blogPosts.data.items.map(blogPost => ({ params: { slug: blogPost.elements.url_slug.value }, props: { blogPost } }))}
const blogPost: BlogPost = Astro.props.blogPost---<html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>{blogPost.elements.title.value}</title> </head> <body> <article> <h1>{blogPost.elements.title.value}</h1> <Fragment set:html={blogPost.elements.teaser.value} /> <Fragment set:html={blogPost.elements.content.value} /> <time>{new Date(blogPost.elements.date.value ?? "")}</time> </body></html>
导航到你的 Astro 预览(默认为 https://:4321/blog/astro-is-amazing/)以查看呈现的博客文章。
按需渲染
标题为“按需渲染”的部分如果你的路由是按需渲染的,你将使用动态路由从 Kontent.ai 获取页面数据。
在 /src/pages/blog/
中创建一个新文件 [slug].astro
并添加以下代码。数据获取与之前的用例非常相似,但增加了一个 equalsFilter
,让我们能够根据使用的 URL 找到正确的博客文章
---import { deliveryClient } from '../../lib/kontent';import type { BlogPost } from '../../models';import { contentTypes } from '../../models/project/contentTypes';
const { slug } = Astro.paramslet blogPost: BlogPost;try { const data = await deliveryClient .items<BlogPost>() .equalsFilter(contentTypes.blog_post.elements.url_slug.codename, slug ?? '') .type(contentTypes.blog_post.codename) .limitParameter(1) .toPromise() blogPost = data.data.items[0]} catch (error) { return Astro.redirect('/404')}---
如果你不使用生成的类型,你可以改用字符串字面量来定义内容项类型和被过滤的元素代码名
const data = await deliveryClient .items() .equalsFilter("url_slug", slug ?? '') .type("blog_post") .limitParameter(1) .toPromise()
最后,添加 HTML 代码来渲染博客文章。这部分与静态生成相同
---import { deliveryClient } from '../../lib/kontent';import type { BlogPost } from '../../models';import { contentTypes } from '../../models/project/contentTypes';
const { slug } = Astro.paramslet blogPost: BlogPost;try { const data = await deliveryClient .items<BlogPost>() .equalsFilter(contentTypes.blog_post.elements.url_slug.codename, slug ?? '') .type(contentTypes.blog_post.codename) .limitParameter(1) .toPromise() blogPost = data.data.items[0]} catch (error) { return Astro.redirect('/404')}---<html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>{blogPost.elements.title.value}</title> </head> <body> <article> <h1>{blogPost.elements.title.value}</h1> <Fragment set:html={blogPost.elements.teaser.value} /> <Fragment set:html={blogPost.elements.content.value} /> <time>{new Date(blogPost.elements.date.value ?? '')}</time> </body></html>
发布你的站点
标题为“发布你的站点”的部分要部署你的网站,请访问部署指南并按照你首选的托管提供商的说明进行操作。
在 Kontent.ai 内容更改时重新构建
标题为“在 Kontent.ai 内容更改时重新构建”的部分如果你的项目使用 Astro 的默认静态模式,当你的内容发生变化时,你需要设置一个 webhook 来触发新的构建。如果你使用 Netlify 或 Vercel 作为托管提供商,你可以使用它们的 webhook 功能从 Kontent.ai 事件触发新的构建。
Netlify
标题为“Netlify”的部分在 Netlify 中设置 webhook
-
转到你的站点仪表盘,然后点击 Build & deploy。
-
在 Continuous Deployment 选项卡下,找到 Build hooks 部分,然后点击 Add build hook。
-
为你的 webhook 提供一个名称,并选择你想要触发构建的分支。点击 Save 并复制生成的 URL。
Vercel
标题为“Vercel”的部分在 Vercel 中设置 webhook
-
转到你的项目仪表盘,然后点击 Settings。
-
在 Git 选项卡下,找到 Deploy Hooks 部分。
-
为你的 webhook 提供一个名称,并选择你想要触发构建的分支。点击 Add 并复制生成的 URL。
向 Kontent.ai 添加 webhook
标题为“向 Kontent.ai 添加 webhook”的部分在 Kontent.ai 应用中,转到 Environment settings -> Webhooks。点击 Create new webhook 并为你的新 webhook 提供一个名称。粘贴你从 Netlify 或 Vercel 复制的 URL,并选择哪些事件应该触发该 webhook。默认情况下,为了在已发布内容更改时重建你的站点,你只需要 Delivery API triggers 下的 Publish 和 Unpublish 事件。完成后,点击 Save。
现在,每当你在 Kontent.ai 中发布一篇新的博客文章时,都会触发一个新的构建,你的博客将会更新。