跳转到内容

Strapi & Astro

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

本指南将构建一个包装函数来将 Strapi 与 Astro 连接起来。

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

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

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

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

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

如果你希望为你的环境变量提供智能提示(IntelliSense),你可以在 src/ 目录中创建一个 env.d.ts 文件,并像这样配置 ImportMetaEnv

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

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

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

src/lib/strapi.ts 创建一个新文件,并添加以下包装函数以与 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;
}

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

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

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

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;
};
}
  • 目录src/
    • 目录interfaces/
      • article.ts
    • 目录lib/
      • strapi.ts
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json
  1. 更新你的主页 src/pages/index.astro 以显示博客文章列表,每篇文章都带有描述和指向其自己页面的链接。

  2. 导入包装函数和接口。添加以下 API 调用来获取你的文章并返回一个列表

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

    该 API 调用从 https://:1337/api/articles 请求数据,并返回 articles:一个代表你数据的 JSON 对象数组

    [
    {
    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. 使用 API 返回的 articles 数组中的数据,以列表形式显示你的 Strapi 博客文章。这些文章将链接到它们各自的独立页面,你将在下一步中创建这些页面。

    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>

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

  • 目录src/
    • 目录interfaces/
      • article.ts
    • 目录lib/
      • strapi.ts
    • 目录pages/
      • index.astro
      • 目录blog/
        • [slug].astro
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

在 Astro 的默认静态模式 (SSG) 下,使用 getStaticPaths() 从 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;
---

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

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>

如果你已经通过适配器选择了按需渲染,请使用以下代码生成你的动态路由

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

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

由于你正在使用重定向到 /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>
<img src="https://http.cat/404" />
</body>
</html>

如果找不到文章,用户将被重定向到这个 404 页面,并看到一只可爱的猫。

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

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

在 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。

按照 Strapi webhooks 指南在你的 Strapi 管理面板中创建一个 webhook。

更多 CMS 指南

贡献 社区 赞助