跳转到内容

Storyblok & Astro

Storyblok 是一个基于组件的无头(Headless)CMS,它允许你使用名为 Bloks 的可重用组件来管理你的内容。

在本节中,你将使用 Storyblok 集成 将 Storyblok 连接到 Astro。

要开始,你需要具备以下条件

  1. 一个 Astro 项目 - 如果你还没有 Astro 项目,我们的安装指南将帮助你快速启动并运行。

  2. 一个 Storyblok 账户和空间 - 如果你还没有账户,可以免费注册并创建一个新空间。

  3. Storyblok 预览令牌(Preview token) - 此令牌将用于获取内容的草稿和已发布版本。你可以在 Storyblok 空间设置的 Access Tokens 选项卡中找到并生成你的 API 令牌。

要将你的 Storyblok 凭据添加到 Astro,请在项目根目录中创建一个 .env 文件,并包含以下变量:

.env
STORYBLOK_TOKEN=YOUR_PREVIEW_TOKEN

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

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

  • 目录src/
  • .env
  • astro.config.mjs
  • package.json

要将 Astro 与你的 Storyblok 空间连接,请使用下面适用于你首选包管理器的命令来安装官方 Storyblok 集成

终端窗口
npm install @storyblok/astro vite

修改你的 Astro 配置文件以包含 Storyblok 集成:

astro.config.mjs
import { defineConfig } from 'astro/config';
import { storyblok } from '@storyblok/astro';
import { loadEnv } from 'vite';
const env = loadEnv("", process.cwd(), 'STORYBLOK');
export default defineConfig({
integrations: [
storyblok({
accessToken: env.STORYBLOK_TOKEN,
components: {
// Add your components here
},
apiOptions: {
// Choose your Storyblok space region
region: 'us', // optional, or 'eu' (default)
},
})
],
});

Storyblok 集成需要一个包含以下属性的对象:

  1. accessToken - 引用你在上一步中添加的 Storyblok API 令牌。

  2. components - 一个将 Storyblok 组件名称映射到本地组件路径的对象。这是在 Astro 中渲染 Storyblok Bloks 所必需的。

  3. apiOptions - 包含 Storyblok API 选项的对象。

要将你的 Bloks 连接到 Astro,请在 src 目录中创建一个名为 storyblok 的新文件夹。该文件夹将包含所有与你 Storyblok Blok 库中的 Bloks 相匹配的 Astro 组件。

在此示例中,你的 Storyblok 库中有一个 blogPost Blok 内容类型,它具有以下字段:

  • title - 文本字段
  • description - 文本字段
  • content - 富文本字段

我们的目标是创建等效的 Astro 组件,该组件将使用这些字段来渲染其内容。为此,请在 src/storyblok 内创建一个名为 BlogPost.astro 的新文件,内容如下:

src/storyblok/BlogPost.astro
---
import { storyblokEditable, renderRichText } from '@storyblok/astro'
const { blok } = Astro.props
const content = renderRichText(blok.content)
---
<article {...storyblokEditable(blok)}>
<h1>{blok.title}</h1>
<p>{blok.description}</p>
<Fragment set:html={content} />
</article>

blok 属性包含你将从 Storyblok 收到的数据。它还包含在 Storyblok 中 blogPost 内容类型 Blok 中定义的字段。

为了渲染我们的内容,该集成提供了如下工具函数:

  • storyblokEditable - 它为元素添加必要的属性,以便你可以在 Storyblok 中编辑它们。
  • renderRichText - 它将富文本字段转换为 HTML。

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

  • 目录src/
    • 目录storyblok/
      • BlogPost.astro
  • .env
  • astro.config.mjs
  • package.json

最后,要将 blogPost Blok 连接到 BlogPost 组件,请在 Astro 配置文件中的 components 对象中添加一个新属性。

  • 键(key)是 Storyblok 中 Blok 的名称。在本例中,它是 blogPost
  • 值(value)是组件的路径。在本例中,它是 storyblok/BlogPost
astro.config.mjs
import { defineConfig } from 'astro/config';
import { storyblok } from '@storyblok/astro';
import { loadEnv } from 'vite';
const env = loadEnv("", process.cwd(), 'STORYBLOK');
export default defineConfig({
integrations: [
storyblok({
accessToken: env.STORYBLOK_TOKEN,
components: {
blogPost: 'storyblok/BlogPost',
},
apiOptions: {
region: 'us',
},
})
],
});

为了测试设置,请在 Storyblok 中使用 blogPost 内容类型创建一个名为 test-post 的新 story。在 Astro 中,请在 src/pages/ 目录中创建一个名为 test-post.astro 的新页面,内容如下:

src/pages/test-post.astro
---
import { useStoryblokApi } from '@storyblok/astro'
import StoryblokComponent from '@storyblok/astro/StoryblokComponent.astro'
const storyblokApi = useStoryblokApi()
const { data } = await storyblokApi.get("cdn/stories/test-post", {
version: import.meta.env.DEV ? "draft" : "published",
});
const content = data.story.content;
---
<StoryblokComponent blok={content} />

要查询你的数据,请使用 useStoryblokApi 钩子(hook)。这将使用你的集成配置来初始化一个新的客户端实例。

要渲染你的内容,请将 Story 的 content 属性作为 blok prop 传递给 StoryblokComponent。此组件将渲染定义在 content 属性内部的 Bloks。在本例中,它将渲染 BlogPost 组件。

集成设置完成后,你现在可以开始使用 Astro 和 Storyblok 创建一个博客了。

  1. 一个 Storyblok 空间 - 对于本教程,我们建议使用一个新空间。如果你已经有一个包含 Bloks 的空间,可以随时使用它们,但你需要修改代码以匹配 Blok 名称和内容类型。

  2. 一个与 Storyblok 集成的 Astro 项目 - 请参阅与 Astro 集成部分,了解如何设置集成的说明。

要创建 Bloks,请前往 Storyblok 应用并点击 Block Library 选项卡。点击 + New blok 按钮并创建以下 Bloks:

  1. blogPost - 一个内容类型 Blok,具有以下字段:

    • title - 文本字段
    • description - 文本字段
    • content - 富文本字段
  2. blogPostList - 一个空的可嵌套(nestable) Blok

  3. page - 一个内容类型 Blok,具有以下字段:

    • body - 一个可嵌套(nestable) Blok

要添加新内容,请点击 Content 选项卡进入内容部分。使用你在上一步创建的 Blok 库,创建以下 stories:

  1. home - 一个使用 page Blok 的内容类型 story。在 body 字段内,添加一个 blogPostList Blok。

  2. blog/no-javascript - 一个位于 blog 文件夹内,使用 blogPost 内容类型的 story。

    title: No JavaScript
    description: A sample blog post
    content: Hi there! This blog post doesn't use JavaScript.
  3. blog/astro-is-amazing - 一个位于 blog 文件夹内,使用 blogPost 内容类型的 story。

    title: Astro is amazing
    description: We love Astro
    content: Hi there! This blog post was build with Astro.

现在你的内容已经准备好了,返回到你的 Astro 项目并开始构建你的博客。

要将你新创建的 Bloks 连接到 Astro 组件,请在 src 目录中创建一个名为 storyblok 的新文件夹,并添加以下文件:

Page.astro 是一个可嵌套的 Blok 内容类型组件,它将递归地渲染 page Blok 的 body 属性内的所有 Bloks。它还会将 storyblokEditable 属性添加到父元素,这将允许我们在 Storyblok 中编辑页面。

src/storyblok/Page.astro
---
import { storyblokEditable } from '@storyblok/astro'
import StoryblokComponent from "@storyblok/astro/StoryblokComponent.astro";
const { blok } = Astro.props
---
<main {...storyblokEditable(blok)}>
{
blok.body?.map((blok) => {
return <StoryblokComponent blok={blok} />
})
}
</main>

BlogPost.astro 将渲染 blogPost Blok 的 titledescriptioncontent 属性。

要将 content 属性从富文本字段转换为 HTML,你可以使用 renderRichText 辅助函数。

src/storyblok/BlogPost.astro
---
import { storyblokEditable, renderRichText } from '@storyblok/astro'
const { blok } = Astro.props
const content = renderRichText(blok.content)
---
<article {...storyblokEditable(blok)}>
<h1>{blok.title}</h1>
<p>{blok.description}</p>
<Fragment set:html={content} />
</article>

BlogPostList.astro 是一个可嵌套的 Blok 内容类型组件,它将渲染一个博客文章预览列表。

它使用 useStoryblokApi 钩子来获取所有内容类型为 blogPost 的 stories。它使用 version 查询参数,在开发模式下获取 stories 的草稿版本,在生产构建时获取已发布版本。

Astro.props 用于在 Storyblok 中设置编辑器。如果需要,也可以在这里向你的组件传递额外的 props。

src/storyblok/BlogPostList.astro
---
import { storyblokEditable } from '@storyblok/astro'
import { useStoryblokApi } from '@storyblok/astro'
const storyblokApi = useStoryblokApi();
const { data } = await storyblokApi.get('cdn/stories', {
version: import.meta.env.DEV ? "draft" : "published",
content_type: 'blogPost',
})
const posts = data.stories.map(story => {
return {
title: story.content.title,
date: new Date(story.published_at).toLocaleDateString("en-US", {dateStyle: "full"}),
description: story.content.description,
slug: story.full_slug,
}
})
const { blok } = Astro.props
---
<ul {...storyblokEditable(blok)}>
{posts.map(post => (
<li>
<time>{post.date}</time>
<a href={post.slug}>{post.title}</a>
<p>{post.description}</p>
</li>
))}
</ul>

最后,将你的组件添加到 astro.config.mjsstoryblok 配置对象的 components 属性中。键是 Storyblok 中 Blok 的名称,值是相对于 src 的组件路径。

astro.config.mjs
import { defineConfig } from 'astro/config';
import { storyblok } from '@storyblok/astro';
import { loadEnv } from 'vite';
const env = loadEnv("", process.cwd(), 'STORYBLOK');
export default defineConfig({
integrations: [
storyblok({
accessToken: env.STORYBLOK_TOKEN,
components: {
blogPost: 'storyblok/BlogPost',
blogPostList: 'storyblok/BlogPostList',
page: 'storyblok/Page',
},
apiOptions: {
region: 'us',
},
})
],
});

要为特定的 page 创建路由,你可以直接从 Storyblok API 获取其内容,并将其传递给 StoryblokComponent 组件。请记住确保你已经将 Page 组件添加到了你的 astro.config.mjs 中。

src/pages/ 中创建一个 index.astro 文件来渲染 home 页面:

src/pages/index.astro
---
import { useStoryblokApi } from '@storyblok/astro'
import StoryblokComponent from '@storyblok/astro/StoryblokComponent.astro'
import BaseLayout from '../layouts/BaseLayout.astro'
const storyblokApi = useStoryblokApi();
const { data } = await storyblokApi.get('cdn/stories/home', {
version: import.meta.env.DEV ? "draft" : "published",
});
const content = data.story.content;
---
<html lang="en">
<head>
<title>Storyblok & Astro</title>
</head>
<body>
<StoryblokComponent blok={content} />
</body>
</html>

要为所有博客文章生成页面,请创建一个 .astro 页面来创建动态路由。这种方法根据你的路由是预渲染(Astro 中的默认设置)还是按需渲染而有所不同。

如果你正在使用 Astro 的默认静态站点生成模式,你将使用动态路由getStaticPaths 函数来生成你的项目页面。

创建一个新目录 src/pages/blog/ 并添加一个名为 [...slug].astro 的新文件,代码如下:

src/pages/blog/[...slug].astro
---
import { useStoryblokApi } from '@storyblok/astro'
import StoryblokComponent from '@storyblok/astro/StoryblokComponent.astro'
export async function getStaticPaths() {
const sbApi = useStoryblokApi();
const { data } = await sbApi.get("cdn/stories", {
content_type: "blogPost",
version: import.meta.env.DEV ? "draft" : "published",
});
const stories = Object.values(data.stories);
return stories.map((story) => {
return {
params: { slug: story.slug },
};
});
}
const sbApi = useStoryblokApi();
const { slug } = Astro.params;
const { data } = await sbApi.get(`cdn/stories/blog/${slug}`, {
version: import.meta.env.DEV ? "draft" : "published",
});
const story = data.story;
---
<html lang="en">
<head>
<title>Storyblok & Astro</title>
</head>
<body>
<StoryblokComponent blok={story.content} />
</body>
</html>

此文件将为每个 story 生成一个页面,其 slug 和内容从 Storyblok API 获取。

如果你正在使用适配器按需渲染你的路由,你将使用动态路由从 Storyblok 获取页面数据。

创建一个新目录 src/pages/blog/ 并添加一个名为 [...slug].astro 的新文件,代码如下:

src/pages/blog/[...slug].astro
---
import { useStoryblokApi } from '@storyblok/astro'
import StoryblokComponent from '@storyblok/astro/StoryblokComponent.astro'
const storyblokApi = useStoryblokApi()
const slug = Astro.params.slug;
let content;
try {
const { data } = await storyblokApi.get(`cdn/stories/blog/${slug}`, {
version: import.meta.env.DEV ? "draft" : "published",
});
content = data.story.content
} catch (error) {
return Astro.redirect('/404')
}
---
<html lang="en">
<head>
<title>Storyblok & Astro</title>
</head>
<body>
<StoryblokComponent blok={content} />
</body>
</html>

此文件将从 Storyblok 获取并渲染与动态 slug 参数匹配的页面数据。

由于你使用了到 /404 的重定向,请在 src/pages 中创建一个 404 页面:

src/pages/404.astro
<html lang="en">
<head>
<title>Not found</title>
</head>
<body>
<p>Sorry, this page does not exist.</p>
</body>
</html>

如果找不到 story,请求将被重定向到 404 页面。

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

如果你的项目使用 Astro 的默认静态模式,你需要设置一个 webhook,以便在内容发生变化时触发新的构建。如果你使用 Netlify 或 Vercel 作为托管提供商,你可以使用它们的 webhook 功能来通过 Storyblok 事件触发新的构建。

在 Netlify 中设置 webhook

  1. 转到你的站点仪表盘,然后点击 Build & deploy

  2. Continuous Deployment 选项卡下,找到 Build hooks 部分,然后点击 Add build hook

  3. 为你的 webhook 提供一个名称,并选择你想要触发构建的分支。点击 Save 并复制生成的 URL。

在 Vercel 中设置 webhook

  1. 转到你的项目仪表盘,然后点击 Settings

  2. Git 选项卡下,找到 Deploy Hooks 部分。

  3. 为你的 webhook 提供一个名称,并选择你想要触发构建的分支。点击 Add 并复制生成的 URL。

在你的 Storyblok 空间的 Settings 中,点击 Webhooks 选项卡。将你复制的 webhook URL 粘贴到 Story published & unpublished 字段中,然后点击 Save 创建一个 webhook。

现在,每当你发布一个新的 story,就会触发一次新的构建,你的博客将会被更新。

  • Storyblok 提供了一个 Astro 集成,可用于将 Storyblok 添加到你的项目中。

更多 CMS 指南

贡献 社区 赞助