Skip to content

Strapi 和 Astro

Strapi 是一个开源、可定制、无头 CMS。

¥Strapi is an open-source, customizable, headless CMS.

¥Integrating with Astro

本指南将构建一个封装函数来连接 Strapi 和 Astro。

¥This guide will build a wrapper function to connect Strapi with Astro.

¥Prerequisites

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

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

  1. Astro 项目 - 如果你还没有 Astro 项目,我们的 安装指南 将立即帮助你启动并运行。
  2. Strapi CMS 服务器 - 你可以 在本地环境中设置 Strapi 服务器

.env 中添加 Strapi URL

标题部分 在 .env 中添加 Strapi URL

¥Adding the Strapi URL in .env

要将 Strapi URL 添加到 Astro,请在项目的根目录中创建一个 .env 文件(如果尚不存在)并添加以下变量:

¥To add your Strapi URL to Astro, create a .env file in the root of your project (if one does not already exist) and add the following variable:

.env
STRAPI_URL="http://127.0.0.1:1337" # or use your IP address

重新启动开发服务器以在你的 Astro 项目中使用此环境变量。

¥Restart the dev server to use this environment variable in your Astro project.

如果你希望环境变量具有 IntelliSense,你可以在 src/ 目录中创建一个 env.d.ts 文件并配置 ImportMetaEnv,如下所示:

¥If you would like to have IntelliSense for your environment variable, you can create a env.d.ts file in the src/ directory and configure ImportMetaEnv like this:

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

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

¥Your root directory should now include the new file(s):

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

¥Creating the API wrapper

src/lib/strapi.ts 创建一个新文件并添加以下封装函数以与 Strapi API 交互:

¥Create a new file at src/lib/strapi.ts and add the following wrapper function to interact with the Strapi API:

src/lib/strapi.ts
interface Props {
endpoint: string;
query?: Record<string, string>;
wrappedByKey?: string;
wrappedByList?: boolean;
}
/**
* Fetches data from the Strapi API
* @param endpoint - The endpoint to fetch from
* @param query - The query parameters to add to the url
* @param wrappedByKey - The key to unwrap the response from
* @param wrappedByList - If the response is a list, unwrap it
* @returns
*/
export default async function fetchApi<T>({
endpoint,
query,
wrappedByKey,
wrappedByList,
}: Props): Promise<T> {
if (endpoint.startsWith('/')) {
endpoint = endpoint.slice(1);
}
const url = new URL(`${import.meta.env.STRAPI_URL}/api/${endpoint}`);
if (query) {
Object.entries(query).forEach(([key, value]) => {
url.searchParams.append(key, value);
});
}
const res = await fetch(url.toString());
let data = await res.json();
if (wrappedByKey) {
data = data[wrappedByKey];
}
if (wrappedByList) {
data = data[0];
}
return data as T;
}

该函数需要一个具有以下属性的对象:

¥This function requires an object with the following properties:

  1. endpoint - 你正在获取的端点。
  2. query - 添加到 URL 末尾的查询参数
  3. wrappedByKey - 封装 Response 的对象中的 data 键。
  4. wrappedByList - “unwrap” 的参数是 Strapi 返回的列表,并且仅返回第一项。

可选的:创建文章界面

标题部分 可选的:创建文章界面

¥Optional: Creating the Article interface

如果你使用 TypeScript,请创建以下 Article 接口以对应于 src/interfaces/article.ts 处的 Strapi 内容类型:

¥If you are using TypeScript, create the following Article interface to correspond to the Strapi content types at src/interfaces/article.ts:

src/interfaces/article.ts
export default interface Article {
id: number;
attributes: {
title: string;
description: string;
content: string;
slug: string;
createdAt: string;
updatedAt: string;
publishedAt: string;
};
}
  • Directorysrc/
    • Directoryinterfaces/
      • article.ts
    • Directorylib/
      • strapi.ts
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

¥Displaying a list of articles

  1. Update your home page src/pages/index.astro to display a list of blog posts, each with a description and a link to its own page.

  2. Import the wrapper function and the interface. Add the following API call to fetch your articles and return a list:

    src/pages/index.astro
    ---
    import fetchApi from '../lib/strapi';
    import type Article from '../interfaces/article';
    const articles = await fetchApi<Article[]>({
    endpoint: 'articles', // the content type to fetch
    wrappedByKey: 'data', // the key to unwrap the response
    });
    ---

    The API call requests data from http://localhost:1337/api/articles and returns articles: an array of json objects representing your data:

    [
    {
    id: 1,
    attributes: {
    title: "What's inside a Black Hole",
    description: "Maybe the answer is in this article, or not...",
    content: "Well, we don't know yet...",
    slug: "what-s-inside-a-black-hole",
    createdAt: "2023-05-28T13:19:46.421Z",
    updatedAt: "2023-05-28T13:19:46.421Z",
    publishedAt: "2023-05-28T13:19:45.826Z"
    }
    },
    // ...
    ]
  3. Using data from the articles array returned by the API, display your Strapi blog posts in a list. These posts will link to their own individual pages, which you will create in the next step.

    src/pages/index.astro
    ---
    import fetchApi from '../lib/strapi';
    import type Article from '../interfaces/article';
    const articles = await fetchApi<Article[]>({
    endpoint: 'articles?populate=image',
    wrappedByKey: 'data',
    });
    ---
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <title>Strapi & Astro</title>
    </head>
    <body>
    <main>
    <ul>
    {
    articles.map((article) => (
    <li>
    <a href={`/blog/${article.attributes.slug}/`}>
    {article.attributes.title}
    </a>
    </li>
    ))
    }
    </ul>
    </main>
    </body>
    </html>

¥Generating article pages

为每篇文章创建文件 src/pages/blog/[slug].astro动态生成页面

¥Create the file src/pages/blog/[slug].astro to dynamically generate a page for each article.

  • Directorysrc/
    • Directoryinterfaces/
      • article.ts
    • Directorylib/
      • strapi.ts
    • Directorypages/
      • index.astro
      • Directoryblog/
        • [slug].astro
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

¥Static site generation

在 Astro 的默认静态模式 (SSG) 中,使用 getStaticPaths() 从 Strapi 获取文章列表。

¥In Astro’s default static mode (SSG), use getStaticPaths() to fetch your list of articles from Strapi.

"src/pages/blog/[slug].astro
---
import fetchApi from '../../lib/strapi';
import type Article from '../../interfaces/article';
export async function getStaticPaths() {
const articles = await fetchApi<Article[]>({
endpoint: 'articles',
wrappedByKey: 'data',
});
return articles.map((article) => ({
params: { slug: article.attributes.slug },
props: article,
}));
}
type Props = Article;
const article = Astro.props;
---

使用每个帖子对象的属性为每个页面创建模板。

¥Create the template for each page using the properties of each post object.

"src/pages/blog/[slug].astro
---
import fetchApi from '../../lib/strapi';
import type Article from '../../interfaces/article';
export async function getStaticPaths() {
const articles = await fetchApi<Article[]>({
endpoint: 'articles',
wrappedByKey: 'data',
});
return articles.map((article) => ({
params: { slug: article.attributes.slug },
props: article,
}));
}
type Props = Article;
const article = Astro.props;
---
<!DOCTYPE html>
<html lang="en">
<head>
<title>{article.attributes.title}</title>
</head>
<body>
<main>
<img src={import.meta.env.STRAPI_URL + article.attributes.image.data.attributes.url} />
<h1>{article.attributes.title}</h1>
<!-- Render plain text -->
<p>{article.attributes.content}</p>
<!-- Render markdown -->
<MyMarkdownComponent>
{article.attributes.content}
</MyMarkdownComponent>
<!-- Render html -->
<Fragment set:html={article.attributes.content} />
</main>
</body>
</html>

¥On-demand rendering

如果你使用以下代码 选择使用适配器进行按需渲染生成你的动态路由

¥If you’ve opted into on-demand rendering with an adapter, generate your dynamic routes using the following code:

创建 src/pages/blog/[slug].astro 文件:

¥Create the src/pages/blog/[slug].astro file:

src/pages/blog/[slug].astro
---
import fetchApi from '../../../lib/strapi';
import type Article from '../../../interfaces/article';
const { slug } = Astro.params;
let article: Article;
try {
article = await fetchApi<Article>({
endpoint: 'articles',
wrappedByKey: 'data',
wrappedByList: true,
query: {
'filters[slug][$eq]': slug || '',
},
});
} catch (error) {
return Astro.redirect('/404');
}
---
<!DOCTYPE html>
<html lang="en">
<head>
<title>{article.attributes.title}</title>
</head>
<body>
<main>
<img src={import.meta.env.STRAPI_URL + article.attributes.image.data.attributes.url} />
<h1>{article.attributes.title}</h1>
<!-- Render plain text -->
<p>{article.attributes.content}</p>
<!-- Render markdown -->
<MyMarkdownComponent>
{article.attributes.content}
</MyMarkdownComponent>
<!-- Render html -->
<Fragment set:html={article.attributes.content} />
</main>
</body>
</html>

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

¥This file will fetch and render the page data from Strapi 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>
<img src="https://http.cat/404" />
</body>
</html>

如果没有找到该文章,用户将被重定向到这个 404 页面,并受到一只可爱的猫的欢迎。

¥If the article is not found, the user will be redirected to this 404 page and be greeted by a lovely cat.

¥Publishing your site

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

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

¥Rebuild on changes

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

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

要在 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 Strapi

按照 Strapi webhooks 指南 在 Strapi 管理面板中创建 Webhook。

¥Follow the Strapi webhooks guide to create a webhook in your Strapi admin panel.

¥Official Resources

更多 CMS 指南

Astro 中文网 - 粤ICP备13048890号