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" // 或者使用你的 IP 地址重启开发服务器以在你的 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 包装器在 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,可以基于以下 Article 接口来创建 src/interfaces/article.ts,用于响应 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 调用来获取你的文章并返回一个列表: 
---import fetchApi from '../lib/strapi';import type Article from '../interfaces/article';
const articles = await fetchApi<Article[]>({  endpoint: 'articles', // 需要获取的内容类型  wrappedByKey: 'data', // 在响应中被拆封的键});---该 API 调用请求会从 http://localhost: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...',      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'      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 博客文章。这些文章将链接到它们各自的页面,在下一步中你将创建这些页面。
---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>
      <!-- 渲染纯文本 -->      <p>{article.attributes.content}</p>      <!-- 渲染 Markdown -->      <MyMarkdownComponent>        {article.attributes.content}      </MyMarkdownComponent>      <!-- 渲染 HTML -->      <Fragment set:html={article.attributes.content} />    </main>  </body></html>请确保选择适合你内容的正确渲染方式。对于 markdown,请查看我们的markdown 指南。如果你要渲染 html,请参考这个指南以确保安全。
服务器端渲染
段落标题 服务器端渲染如果你选择了 SSR 模式 并设置了 output: server 或 output: hybrid,那么可以使用以下代码来生成你的动态路由。
创建 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>
      <!-- 渲染纯文本 -->      <p>{article.attributes.content}</p>      <!-- 渲染 Markdown -->      <MyMarkdownComponent>        {article.attributes.content}      </MyMarkdownComponent>      <!-- 渲染 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 Webhook 指南在 Strapi 管理面板中创建一个 Webhook。
官方资源
段落标题 官方资源- Strapi 提供的用于 React 的博客指南
