Strapi & Astro
Strapi 是一个开源、可定制的无头 CMS。
与 Astro 集成
名为“与 Astro 集成”的部分本指南将构建一个包装函数来将 Strapi 与 Astro 连接起来。
先决条件
名为“前提条件”的部分要开始,你需要具备以下条件
- 一个 Astro 项目 - 如果你还没有 Astro 项目,我们的安装指南将帮助你快速启动并运行。
- 一个 Strapi CMS 服务器 - 你可以在本地环境上设置一个 Strapi 服务器。
在 .env
中添加 Strapi URL
名为“在 .env 中添加 Strapi URL”的部分要将你的 Strapi URL 添加到 Astro,请在项目根目录中创建一个 .env
文件(如果尚不存在),并添加以下变量
STRAPI_URL="http://127.0.0.1:1337" # or use your IP address
重启开发服务器以在你的 Astro 项目中使用此环境变量。
如果你希望为你的环境变量提供智能提示(IntelliSense),你可以在 src/
目录中创建一个 env.d.ts
文件,并像这样配置 ImportMetaEnv
interface ImportMetaEnv { readonly STRAPI_URL: string;}
现在你的根目录应包含这些新文件
目录src/
- env.d.ts
- .env
- astro.config.mjs
- package.json
创建 API 包装器
名为“创建 API 包装器”的部分在 src/lib/strapi.ts
创建一个新文件,并添加以下包装函数以与 Strapi API 交互
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;}
此函数需要一个具有以下属性的对象
endpoint
- 你正在获取的端点。query
- 要添加到 URL 末尾的查询参数wrappedByKey
- 包装你的Response
的对象中的data
键。wrappedByList
- 一个用于“解包” Strapi 返回的列表并仅返回第一项的参数。
可选:创建 Article 接口
名为“可选:创建 Article 接口”的部分如果你正在使用 TypeScript,请在 src/interfaces/article.ts
中创建以下 Article 接口,以对应 Strapi 的内容类型
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
显示文章列表
名为“显示文章列表”的部分-
更新你的主页
src/pages/index.astro
以显示博客文章列表,每篇文章都带有描述和指向其自己页面的链接。 -
导入包装函数和接口。添加以下 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 fetchwrappedByKey: '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"}},// ...] -
使用 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 获取你的文章列表。
---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;---
使用每个文章对象的属性为每个页面创建模板。
---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>
确保为你的内容选择正确的渲染方式。对于 markdown,请查看我们的 markdown 指南。如果你正在渲染 HTML,请参考本指南以确保安全。
按需渲染
名为“按需渲染”的部分如果你已经通过适配器选择了按需渲染,请使用以下代码生成你的动态路由
创建 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 页面
<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
名为“Netlify”的部分在 Netlify 中设置 webhook
-
转到你的站点仪表盘,然后点击 Build & deploy。
-
在 Continuous Deployment 选项卡下,找到 Build hooks 部分,然后点击 Add build hook。
-
为你的 webhook 提供一个名称,并选择你想要触发构建的分支。点击 Save 并复制生成的 URL。
Vercel
名为“Vercel”的部分在 Vercel 中设置 webhook
-
转到你的项目仪表盘,然后点击 Settings。
-
在 Git 选项卡下,找到 Deploy Hooks 部分。
-
为你的 webhook 提供一个名称,并选择你想要触发构建的分支。点击 Add 并复制生成的 URL。
向 Strapi 添加 webhook
名为“向 Strapi 添加 webhook”的部分按照 Strapi webhooks 指南在你的 Strapi 管理面板中创建一个 webhook。
官方资源
名为“官方资源”的部分- Strapi 官方的 React Strapi 博客指南