Skip to content

Storyblok 和 Astro

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

¥Storyblok is a component-based headless CMS that allows you to manage your content using reusable components called Bloks.

¥Integrating with Astro

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

¥In this section, you will use the Storyblok integration to connect Storyblok to Astro.

¥Prerequisites

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

¥To get started, you will need to have the following:

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

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

  3. Storyblok 预览令牌 - 此令牌将用于获取内容的草稿和已发布版本。你可以在 Storyblok 空间设置的“访问令牌”选项卡中查找并生成 API 令牌。

¥Setting up credentials

要将 Storyblok 凭据添加到 Astro,请使用以下变量在项目的根目录中创建 .env 文件:

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

.env
STORYBLOK_TOKEN=YOUR_PREVIEW_TOKEN

现在,你应该能够在项目中使用这些环境变量。

¥Now, you should be able to use these environment variables in your project.

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

¥Your root directory should now include this new file:

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

¥Installing dependencies

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

¥To connect Astro with your Storyblok space, install the official Storyblok integration using the command below for your preferred package manager:

终端窗口
npm install @storyblok/astro vite

¥Configuring Storyblok

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

¥Modify your Astro config file to include the Storyblok integration:

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 集成需要具有以下属性的对象:

¥The Storyblok integration requires an object with the following properties:

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

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

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

将 Bloks 连接到 Astro 组件

标题部分 将 Bloks 连接到 Astro 组件

¥Connecting Bloks to Astro components

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

¥To connect your Bloks to Astro, create a new folder named storyblok in the src directory. This folder will contain all the Astro components that will match your Bloks in your Storyblok Blok library.

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

¥In this example, you have a blogPost Blok content type in your Storyblok library with the following fields:

  • title - 文本字段

  • description - 文本字段

  • content - 富文本字段

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

¥Our goal is to create the equivalent Astro component that will use these fields to render its content. To do this, create a new file named BlogPost.astro inside src/storyblok with the following content:

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 中定义的字段。

¥The blok property contains the data that you will receive from Storyblok. It also contains the fields that were defined in the blogPost content type Blok in Storyblok.

为了渲染我们的内容,集成提供了实用功能,例如:

¥To render our content, the integration provides utility functions such as:

  • storyblokEditable - 它将必要的属性添加到元素中,以便你可以在 Storyblok 中编辑它们。

  • renderRichText - 它将富文本字段转换为 HTML。

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

¥Your root directory should include this new file:

  • Directorysrc/
    • Directorystoryblok/
      • BlogPost.astro
  • .env
  • astro.config.mjs
  • package.json

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

¥Finally, to connect the blogPost Blok to the BlogPost component, add a new property to your components object in your Astro config file.

  • 关键是 Storyblok 中 Blok 的名称。在本例中,它是 blogPost

  • 该值是组件的路径。在本例中,它是 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',
},
})
],
});

¥Fetching data

要测试设置,请在 Storyblok 中创建一个名为 test-postblogPost 内容类型的新故事。在 Astro 中,在 src/pages/ 目录中创建一个名为 test-post.astro 的新页面,其中包含以下内容:

¥To test the setup, in Storyblok create a new story with the blogPost content type named test-post. In Astro, create a new page in the src/pages/ directory named test-post.astro with the following content:

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 钩子。这将使用你的集成配置初始化一个新的客户端实例。

¥To query your data, use the useStoryblokApi hook. This will initialize a new client instance using your integration configuration.

要渲染你的内容,请将 Story 的 content 属性作为 blok prop 传递给 StoryblokComponent。该组件将渲染在 content 属性内定义的 Bloks。在这种情况下,它将渲染 BlogPost 组件。

¥To render your content, pass the content property of the Story to the StoryblokComponent as a blok prop. This component will render the Bloks that are defined inside the content property. In this case, it will render the BlogPost component.

使用 Astro 和 Storyblok 制作博客

标题部分 使用 Astro 和 Storyblok 制作博客

¥Making a blog with Astro and Storyblok

设置集成后,你现在可以使用 Astro 和 Storyblok 创建博客。

¥With the integration set up, you can now create a blog with Astro and Storyblok.

¥Prerequisites

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

  2. 与 Storyblok 集成的 Astro 项目 - 请参阅 与 Astro 集成 以了解如何设置集成的说明。

¥Creating a blok library

要创建 Bloks,请转到 Storyblok 应用并单击“Block Library”选项卡。单击 + New blok 按钮并创建以下 Bloks:

¥To create Bloks, go to the Storyblok app and click on the Block Library tab. Click on the + New blok button and create the following Bloks:

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

    • title - 文本字段

    • description - 文本字段

    • content - 富文本字段

  2. blogPostList - 一个空的可嵌套的 Blok

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

    • body - 可嵌套的布洛克

¥Creating content

要添加新内容,请单击“内容”选项卡转到内容部分。使用你在上一步中创建的 Blok 库,创建以下故事:

¥To add new content, go to the content section by clicking on the Content tab. Using the Blok library that you created in the previous step, create the following stories:

  1. home - 与 page Blok 相关的内容类型故事。在 body 字段内,添加 blogPostList 块。

  2. blog/no-javascript - 博客文件夹内内容类型为 blogPost 的故事。

    title: No JavaScript
    description: A sample blog post
    content: Hi there! This blog post doesn't use JavaScript.
  3. blog/astro-is-amazing - 博客文件夹内内容类型为 blogPost 的故事。

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

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

¥Now that you have your content ready, return to your Astro project and start building your blog.

¥Connecting Bloks to components

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

¥To connect your newly created Bloks to Astro components, create a new folder named storyblok in your src directory and add the following files:

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

¥Page.astro is a nestable Block content type component that will recursively render all the Bloks inside the body property of the page Blok. It also adds the storyblokEditable attributes to the parent element which will allow us to edit the page in 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 块的 titledescriptioncontent 属性。

¥BlogPost.astro will render the title, description and content properties of the blogPost Blok.

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

¥To transform the content property from a rich text field to HTML, you can use the renderRichText helper function.

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 内容类型组件,它将渲染博客文章预览列表。

¥BlogPostList.astro is a nestable Blok content type component that will render a list of blog post previews.

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

¥It uses the useStoryblokApi hook to fetch all the stories with the content type of blogPost. It uses the version query parameter to fetch the draft versions of the stories when in development mode and the published versions when building for production.

Astro.props 用于在 Storyblok 中设置编辑器。如果需要,还可以在此处将其他属性传递给你的组件。

¥Astro.props is used to set up the editor in Storyblok. Additional props can also be passed to your component here, if needed.

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 的路径。

¥Finally, add your components to the components property of the storyblok config object in astro.config.mjs. The key is the name of the Blok in Storyblok, and the value is the path to the component relative to 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',
},
})
],
});

¥Generating pages

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

¥To create a route for a specific page, you can fetch its content directly from the Storyblok API and pass it to the StoryblokComponent component. Remember to make sure you have added the Page component to your astro.config.mjs.

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

¥Create an index.astro file in src/pages/ to render the home page:

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 中的默认设置)还是 渲染于需求

¥To generate pages for all of your blog posts, create a .astro page that will create dynamic routes. This approach varies depending on whether your routes are prerendered (the default in Astro) or rendered on demand.

¥Static site generation

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

¥If you are using Astro’s default static site generation, you will use dynamic routes and the getStaticPaths function to generate your project pages.

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

¥Create a new directory src/pages/blog/ and add a new file called [...slug].astro with the following code:

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>

该文件将为每个故事生成一个页面,其中包含从 Storyblok API 获取的 slug 和内容。

¥This file will generate a page for each story, with the slug and content fetched from the Storyblok API.

¥On-demand rendering

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

¥If you are rendering your routes on demand with an adapter, you will use dynamic routes to fetch the page data from Storyblok.

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

¥Create a new directory src/pages/blog/ and add a new file called [...slug].astro with the following code:

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 参数匹配的页面数据。

¥This file will fetch and render the page data from Storyblok that matches the dynamic slug parameter.

由于你使用的是重定向到 /404,因此在 src/pages 中创建 404 页面:

¥Since you are using a redirect to /404, create a 404 page in src/pages:

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

如果没有找到故事,请求将被重定向到 404 页面。

¥If the story is not found, the request will be redirected to the 404 page.

¥Publishing your site

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

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

根据 Storyblok 更改进行重建

标题部分 根据 Storyblok 更改进行重建

¥Rebuild on Storyblok changes

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

¥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 Storyblok 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.

将 webhook 添加到 Storyblok
标题部分 将 webhook 添加到 Storyblok

¥Adding a webhook to Storyblok

在你的 Storyblok 空间设置中,单击 Webhooks 选项卡。将你复制的 webhook URL 粘贴到 Story 已发布和未发布字段中,然后点击 Save 创建 webhook。

¥In your Storyblok space Settings, click on the Webhooks tab. Paste the webhook URL you copied in the Story published & unpublished field and hit Save to create a webhook.

现在,每当你发布新故事时,都会触发新的构建,并且你的博客也会更新。

¥Now, whenever you publish a new story, a new build will be triggered and your blog will be updated.

¥Official Resources

  • Storyblok 提供了 Astro 整合 将 Storyblok 添加到你的项目中。

¥Community Resources

更多 CMS 指南

Astro 中文网 - 粤ICP备13048890号