Skip to content

Kontent.ai 和 Astro

Kontent.ai 是一款无头 CMS,允许你在 AI 功能的支持下以结构化和模块化的方式管理内容。

¥Kontent.ai is a headless CMS that allows you to manage content in a structured and modular way, supported by AI capabilities.

¥Integrating with Astro

在本部分中,你将使用 Kontent.ai TypeScript SDK 将 Kontent.ai 项目连接到 Astro 应用。

¥In this section, you’ll use the Kontent.ai TypeScript SDK to connect your Kontent.ai project to your Astro application.

¥Prerequisites

首先,你需要具备以下条件:

¥To get started, you’ll need the following:

  1. Kontent.ai 项目 - 如果你还没有 Kontent.ai 账户,请 免费注册 并创建一个新项目。

  2. 交付 API 密钥 - 你将需要已发布内容的环境 ID 和用于获取草稿的预览 API 密钥(可选)。这两个密钥都位于 Kontent.ai 中的“环境设置”->“API 密钥”选项卡中。

¥Setting up credentials

要将你的 Kontent.ai 凭证添加到 Astro,请在项目的根目录中使用以下变量创建一个 .env 文件:

¥To add your Kontent.ai credentials to Astro, create a .env file in the root of your project with the following variables:

.env
KONTENT_ENVIRONMENT_ID=YOUR_ENVIRONMENT_ID
KONTENT_PREVIEW_API_KEY=YOUR_PREVIEW_API_KEY

现在,这些环境变量可以在你的 Astro 项目中使用。

¥Now, these environment variables can be used in your Astro project.

如果你想获得 这些环境变量的 TypeScript IntelliSense,你可以在 src/ 目录中创建一个新的 env.d.ts 文件并配置 ImportMetaEnv,如下所示:

¥If you would like to get TypeScript IntelliSense for these environment variables, you can create a new env.d.ts file in the src/ directory and configure ImportMetaEnv like this:

src/env.d.ts
interface ImportMetaEnv {
readonly KONTENT_ENVIRONMENT_ID: string;
readonly KONTENT_PREVIEW_API_KEY: string;
}

你的根目录现在应该包含这些新文件:

¥Your root directory should now include these new files:

  • Directorysrc/
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

¥Installing dependencies

要将 Astro 与你的 Kontent.ai 项目连接,请安装 Kontent.ai TypeScript SDK

¥To connect Astro with your Kontent.ai project, install the Kontent.ai TypeScript SDK:

Terminal window
npm install @kontent-ai/delivery-sdk

接下来,在 Astro 项目的 src/lib/ 目录中创建一个名为 kontent.ts 的新文件。

¥Next, create a new file called kontent.ts in the src/lib/ directory of your Astro project.

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,
});

此实现使用 .env 文件中的凭据创建一个新的 DeliveryClient 对象。

¥This implementation creates a new DeliveryClient object using credentials from the .env file.

最后,Astro 项目的根目录现在应该包含这些新文件:

¥Finally, the root directory of your Astro project should now include these new files:

  • Directorysrc/
    • Directorylib/
      • kontent.ts
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

¥Fetching data

DeliveryClient 现在可用于所有组件。要获取内容,请使用 DeliveryClient 和方法链接来定义所需的项目。此示例显示了博客文章的基本获取并将其标题渲染在列表中:

¥The DeliveryClient is now available to all components. To fetch content, use the DeliveryClient and method chaining to define your desired items. This example shows a basic fetch of blog posts and renders their titles in a list:

src/pages/index.astro
---
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 文档.txt 中找到更多查询选项。

¥You can find more querying options in the Kontent.ai documentation.

使用 Astro 和 Kontent.ai 制作博客

Section titled 使用 Astro 和 Kontent.ai 制作博客

¥Making a blog with Astro and Kontent.ai

通过上述设置,你现在可以创建一个使用 Kontent.ai 作为内容源的博客。

¥With the setup above, you are now able to create a blog that uses Kontent.ai as the source of content.

¥Prerequisites

  1. Kontent.ai 项目 - 对于本教程,建议使用空白项目。如果你的内容模型中已有某些内容类型,则可以使用它们,但你需要修改代码片段以匹配你的内容模型。

  2. 配置为从 Kontent.ai 获取内容的 Astro 项目 - 有关如何使用 Kontent.ai 设置 Astro 项目的更多详细信息,请参见上文

¥Setting up content model

在 Kontent.ai 中,导航到内容模型并创建一个具有以下字段和值的新内容类型:

¥In Kontent.ai, navigate to Content model and create a new content type with the following fields and values:

  • 名称:博客文章

  • 要素:

    • 文本域

      • 名称:标题

      • 所需元素:yes

    • 富文本字段

      • 名称:预告片

      • 所需元素:yes

      • 该元素中允许:仅检查文本

    • 富文本字段

      • 名称:内容

      • 所需元素:yes

    • 日期和时间字段

      • 名称:日期
    • URL slug 字段

      • 名称:网址段

      • 所需元素:yes

      • 自动生成自:选择 “标题”

然后,单击“保存更改”。

¥Then, click on Save Changes.

¥Creating content

现在,导航到“内容和资源”选项卡并创建一个博客文章类型的新内容项。使用这些值填充字段:

¥Now, navigate to Content & assets tab and create a new content item of type Blog Post. Fill the fields using these values:

  • 内容项目名称:Astro

  • 标题:Astro 太棒了

  • 预告片:Astro 是一个一体化框架,可以更快地构建快速网站。

  • 内容:你可以使用 JavaScript 来实现网站功能,但不需要客户端打包包。

  • 日期与时间:选择今天

  • 网址段:astro-is-amazing

完成后,使用顶部的发布按钮发布博客文章。

¥When you’re finished, publish the blog post using the Publish button at the top.

注意:在转到下一个之前,你可以随意创建任意数量的博客文章步骤。

¥Note: Feel free to create as many blog posts as you like before moving to the next step.

在 TypeScript 中生成内容模型

Section titled 在 TypeScript 中生成内容模型

¥Generating content model in TypeScript

接下来,你将从内容模型中生成 TypeScript 类型。

¥Next, you’ll generate TypeScript types out of your content model.

首先,安装 Kontent.ai JS 模型生成器ts-nodedotenv

¥First, install the Kontent.ai JS model generator, ts-node, and dotenv:

Terminal window
npm install @kontent-ai/model-generator ts-node dotenv

然后,将以下脚本添加到 package.json:

¥Then, add the following script to package.json:

package.json
{
...
"scripts": {
...
"regenerate:models": "ts-node --esm ./generate-models.ts"
},
}

由于这些类型需要有关你的项目的结构信息,而公共 API 中不提供这些信息,因此你还需要将内容管理 API 密钥添加到 .env 文件中。你可以在环境设置 -> API 密钥 -> 管理 API 下生成密钥。

¥Because the types require structural information about your project that is not available in the public API, you also need to add a Content Management API key to the .env file. You can generate the key under Environment settings -> API keys -> Management API.

.env
KONTENT_ENVIRONMENT_ID=YOUR_ENVIRONMENT_ID
KONTENT_PREVIEW_API_KEY=YOUR_PREVIEW_API_KEY
KONTENT_MANAGEMENT_API_KEY=YOUR_MANAGEMENT_API_KEY

最后,添加配置模型生成器的脚本 generate-models.ts 以生成模型:

¥Finally, add the script generate-models.ts that configures the model generator to generate the models:

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
})

现在,执行它:

¥Now, execute it:

Terminal window
npm run regenerate:models

¥Displaying a list of blog posts

现在你已准备好获取一些内容。进入 Astro 页面,你想要显示所有博客文章的列表,例如 src/pages 中的主页 index.astro

¥Now you’re ready to fetch some content. Go to the Astro page where you want to display a list of all blog posts, for example, the homepage index.astro in src/pages.

获取 Astro 页面 frontmatter 中的所有博客文章:

¥Fetch all blog posts in the frontmatter of the Astro page:

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

如果你跳过了模型生成,你还可以使用无类型对象和字符串字面量来定义类型:

¥If you skipped the model generation, you can also use an untyped object and string literal to define the type:

const blogPosts = await deliveryClient
.items()
.type("blogPost")
.toPromise()

fetch 调用将返回一个 response 对象,其中包含 data.items 中所有博客文章的列表。在 Astro 页面的 HTML 部分,你可以使用 map() 函数列出博客文章:

¥The fetch call will return a response object which contains a list of all blog posts in data.items. In the HTML section of the Astro page, you can use the map() function to list the blog posts:

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

¥Generating individual blog posts

本教程的最后一步是生成详细的博客文章页面。

¥The last step of the tutorial is to generate detailed blog post pages.

¥Static site generation

在本部分中,你将结合使用 静态 (SSG) 模式 和 Astro。

¥In this section, you’ll use the Static (SSG) Mode with Astro.

首先,在 /src/pages/blog/ 中创建一个文件 [slug].astro,该文件需要导出一个从 CMS 收集所有数据的函数 getStaticPaths

¥First, create a file [slug].astro in /src/pages/blog/ which needs to export a function getStaticPaths that collects all data from the CMS:

src/pages/blog/[slug].astro
---
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 获取所有博客文章。该代码片段与你在主页上使用的代码片段完全相同。

¥So far, the function fetches all blog posts from Kontent.ai. The code snippet is exactly the same as what you used on the home page.

接下来,该函数必须导出每篇博客文章的路径和数据。你将文件命名为 [slug].astro,因此表示 URL slug 的参数称为 slug

¥Next, the function must export paths and data for each blog post. You named the file [slug].astro, so the param which represents the URL slug is called slug:

src/pages/blog/[slug].astro
---
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 模板并显示每篇博客文章:

¥The last part is to provide the HTML template and display each blog post:

src/pages/blog/[slug].astro
---
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 预览(默认为 http://localhost:4321/blog/astro-is-amazing/)以查看渲染的博客文章。

¥Navigate to your Astro preview (http://localhost:4321/blog/astro-is-amazing/ by default) to see the rendered blog post.

¥Server-side rendering

如果你选择进入 SSR 模式,你将使用动态路由从 Kontent.ai 获取页面数据。

¥If you’ve opted into SSR mode, you will use dynamic routes to fetch the page data from Kontent.ai.

/src/pages/blog/ 中新建文件 [slug].astro,添加以下代码。数据获取与之前的用例非常相似,但添加了 equalsFilter,让我们可以根据使用的 URL 找到正确的博客文章:

¥Create a new file [slug].astro in /src/pages/blog/ and add the following code. The data fetching is very similar to previous use cases but adds an equalsFilter that lets us find the right blog post based on the used URL:

src/pages/blog/[slug].astro
---
import { deliveryClient } from '../../lib/kontent';
import type { BlogPost } from '../../models';
import { contentTypes } from '../../models/project/contentTypes';
const { slug } = Astro.params
let 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')
}
---

如果你不使用生成类型,则可以使用字符串字面量来定义内容项类型和过滤元素代号:

¥If you’re not using generated types, you can instead use string literals to define the content item type and the filtered element codename:

const data = await deliveryClient
.items()
.equalsFilter("url_slug", slug ?? '')
.type("blog_post")
.limitParameter(1)
.toPromise()

最后,添加 HTML 代码来渲染博客文章。这部分与静态生成相同:

¥Lastly, add the HTML code to render the blog post. This part is the same as with static generation:

src/pages/blog/[slug].astro
---
import { deliveryClient } from '../../lib/kontent';
import type { BlogPost } from '../../models';
import { contentTypes } from '../../models/project/contentTypes';
const { slug } = Astro.params
let 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>

¥Publishing your site

要部署你的网站,请访问 部署指南 并按照你首选托管提供商的说明进行操作。

¥To deploy your website, visit the deployment guides and follow the instructions for your preferred hosting provider.

根据 Kontent.ai 更改进行重建

Section titled 根据 Kontent.ai 更改进行重建

¥Rebuild on Kontent.ai changes

如果你的项目使用 Astro 的默认静态模式,则需要设置一个 Webhook 以在内容更改时触发新的构建。如果你使用 Netlify 或 Vercel 作为托管提供商,则可以使用其 Webhook 功能从 Kontent.ai 事件触发新构建。

¥If your project is using Astro’s default static mode, you will need to set up a webhook to trigger a new build when your content changes. If you are using Netlify or Vercel as your hosting provider, you can use its webhook feature to trigger a new build from Kontent.ai events.

要在 Netlify 中设置 Webhook:

¥To set up a webhook in Netlify:

  1. Go to your site dashboard and click on Build & deploy.

  2. Under the Continuous Deployment tab, find the Build hooks section and click on Add build hook.

  3. Provide a name for your webhook and select the branch you want to trigger the build on. Click on Save and copy the generated URL.

要在 Vercel 中设置 Webhook:

¥To set up a webhook in Vercel:

  1. Go to your project dashboard and click on Settings.

  2. Under the Git tab, find the Deploy Hooks section.

  3. Provide a name for your webhook and the branch you want to trigger the build on. Click Add and copy the generated URL.

¥Adding a webhook to Kontent.ai

Kontent.ai 应用 中,转到环境设置 -> Webhook。单击“创建新 webhook”并为新 webhook 提供名称。粘贴从 Netlify 或 Vercel 复制的 URL,然后选择哪些事件应触发 Webhook。默认情况下,要在发布的内容发生变化时重建你的站点,你只需要在 Delivery API 触发器下发布和取消发布事件。完成后,单击“保存”。

¥In the Kontent.ai app, go to Environment settings -> Webhooks. Click on Create new webhook and provide a name for your new webhook. Paste in the URL you copied from Netlify or Vercel and select which events should trigger the webhook. By default, for rebuilding your site when published content changes, you only need Publish and Unpublish events under Delivery API triggers. When you’re finished, click on Save.

现在,每当你在 Kontent.ai 中发布新的博客文章时,都会触发新的构建并更新你的博客。

¥Now, whenever you publish a new blog post in Kontent.ai, a new build will be triggered and your blog will be updated.

More CMS guides

Astro 中文网 - 粤ICP备13048890号