如何在Remix中实现Strapi组件动态渲染及Teaser全局取最新博客
解决方案
要实现Teaser组件在任意页面的Strapi动态sections中都能SSR获取最新3篇博客,按以下步骤操作:
1. 编写获取最新博客的公用函数
创建独立函数用于从Strapi拉取排序后的3篇最新博客,方便多页面复用:
export async function getLatestBlogs() { const token = process.env.STRAPI_API_TOKEN; if (!token) throw new Error("Strapi API Token environment variable is not set."); const path = `/blogs`; const options = { headers: { Authorization: `Bearer ${token}` } }; const urlParamsObject = { sort: ["publishedAt:desc"], // 按发布时间倒序 pagination: { limit: 3 }, // 只取3篇 populate: { thumbnail: { fields: ["url"] }, // 按需获取缩略图字段 seo: { fields: ["title", "description"] } // 可根据实际需求调整 }, }; const baseUrl = getStrapiURL(); const blogs = await fetchAPI(path, urlParamsObject, options); return blogs.data; }
2. 修改页面Loader,并行获取页面与博客数据
在所有可能包含Teaser的页面(首页、关于页等)的Loader中,用Promise.all并行拉取数据,保证SSR且不影响性能:
// 以首页Loader为例 export async function loader({ params }) { // 并行执行两个请求,避免串行等待 const [pageData, latestBlogs] = await Promise.all([ getPageBySlug(params.slug), getLatestBlogs(), ]); return { ...pageData, latestBlogs, // 将博客数据加入返回结果 }; }
如果多个页面需要此逻辑,可用高阶函数封装减少重复代码:
// 公用Loader封装函数 export async function withLatestBlogs(loaderFn) { return async (args) => { const [pageData, latestBlogs] = await Promise.all([ loaderFn(args), getLatestBlogs(), ]); return { ...pageData, latestBlogs }; }; } // 首页Loader使用封装 export const loader = withLatestBlogs(async ({ params }) => { return getPageBySlug(params.slug); });
3. 传递博客数据到Teaser组件
修改页面组件和sectionRenderer,将博客数据传递给Teaser:
修改首页组件
export default function Home() { const { page, latestBlogs } = useLoaderData(); const { attributes } = page || {}; if (attributes) { const { sections } = attributes; return ( <main> {/* 将latestBlogs传给sectionRenderer */} {sections.map((section, index) => sectionRenderer(section, index, latestBlogs) )} </main> ); } else { return <Error />; } }
修改sectionRenderer函数
import Hero from "../components/Hero"; import Teaser from "../components/Teaser"; export function sectionRenderer(section, index, latestBlogs) { switch (section.__component) { case "sections.hero": return <Hero key={index} data={section} />; case "sections.teaser": // 将博客数据传给Teaser组件 return <Teaser key={index} data={section} blogs={latestBlogs} />; default: return null; } }
4. 实现Teaser组件渲染逻辑
在Teaser组件中使用传入的博客数据渲染内容:
import getStrapiURL from "../utils/getStrapiURL"; // 复用你的Strapi URL工具函数 export default function Teaser({ data, blogs }) { // data是Strapi中Teaser区块的配置(如标题、副标题等) const { title, subtitle } = data.attributes; return ( <section className="teaser-container"> <h2>{title}</h2> {subtitle && <p className="teaser-subtitle">{subtitle}</p>} <div className="teaser-blog-grid"> {blogs.map((blog) => { const { id, attributes } = blog; return ( <article key={id} className="teaser-blog-card"> {attributes.thumbnail && ( <img src={`${getStrapiURL()}${attributes.thumbnail.url}`} alt={attributes.title} className="teaser-blog-image" /> )} <h3 className="teaser-blog-title">{attributes.title}</h3> <p className="teaser-blog-excerpt">{attributes.excerpt}</p> <a href={`/blog/${attributes.slug}`} className="teaser-read-more"> 阅读更多 </a> </article> ); })} </div> </section> ); }
注意事项
- 单篇博客详情页等不需要Teaser的页面,无需在Loader中调用
getLatestBlogs,避免不必要的API请求。 - 可根据Strapi中Blog模型的实际字段调整
getLatestBlogs中的populate配置,只获取需要的字段减少数据传输。
内容的提问来源于stack exchange,提问作者emot1onz
相关产品推荐
相关产品推荐

