Next.js 14中generateStaticParams无法传数据引发重复API请求问题
关于Next.js generateStaticParams复用数据的问题
我对Next.js中generateStaticParams的工作方式有疑问:通常我们会获取ID来生成路由段,但获取这些ID的API调用其实已经包含了构建所有页面所需的全部数据,却没法把这些额外数据作为props传给页面组件。
就像我的例子,构建时Next.js会为每个页面调用一次ArticleAPI.fetchByCategory,参数来自generateStaticParams,但明明可以复用generateStaticParams里已经拿到的数据?这样1000个路由就要发1000次请求,而不是一次请求搞定所有页面的数据。
代码示例(路径:/articles/categories/[category]/page.ts)
import { ArticleAPI } from "@/api/article-api"; import { ArticleList } from "@/components/ArticleList/ArticleList"; import { Article, ArticleCategory } from "@/types/article-type"; export async function generateStaticParams() { const articlesByCat = await ArticleAPI.fetchAllCategories(); const params = Object.keys(articlesByCat).map((category) => { const articles = articlesByCat[category]; return { params: { category, articles, // 我本以为可以将其传递给组件? }, }; }); return params; } export default async function ArticlesFilteredByCategories(p: { params: { category: ArticleCategory; articles: Article[] }; }) { // 但这里p.params只包含category,没有articles... // 例如访问/articles/categories/business时,仅得到{ category : "business" } // 我希望能从generateStaticParams接收articles作为props // 而非在这里再次发起请求(若有100个页面,构建时就要发起100次API请求),而数据其实已在generateStaticParams()中获取到了 const articles = await ArticleAPI.fetchByCategory(p.params.category); return ( <ArticleList articles={articles} /> ); }
解决方案:复用请求结果避免重复调用
你没法直接把generateStaticParams里的额外数据传给页面组件,但可以通过缓存来实现数据复用,避免重复发起API请求。
方法1:模块级缓存
在页面模块内维护一个缓存变量,存储fetchAllCategories的结果,页面组件直接读取缓存:
import { ArticleAPI } from "@/api/article-api"; import { ArticleList } from "@/components/ArticleList/ArticleList"; import { Article, ArticleCategory } from "@/types/article-type"; // 模块级缓存,存储全部分类的文章数据 let cachedArticlesByCat: Record<ArticleCategory, Article[]> | null = null; export async function generateStaticParams() { // 首次请求并缓存数据 cachedArticlesByCat = await ArticleAPI.fetchAllCategories(); // 只返回路由需要的category参数 return Object.keys(cachedArticlesByCat).map(category => ({ params: { category } })); } export default async function ArticlesFilteredByCategories(p: { params: { category: ArticleCategory }; }) { // 优先使用缓存,若缓存不存在则重新请求(应对增量静态再生场景) const articlesByCat = cachedArticlesByCat || await ArticleAPI.fetchAllCategories(); const articles = articlesByCat[p.params.category]; return <ArticleList articles={articles} />; }
方法2:API层缓存
在API封装层添加缓存逻辑,让所有调用复用同一份请求结果:
// @/api/article-api.ts let allCategoriesCache: Promise<Record<ArticleCategory, Article[]>> | null = null; export const ArticleAPI = { fetchAllCategories: async () => { if (!allCategoriesCache) { // 仅在首次调用时发起请求 allCategoriesCache = fetch('/api/articles/all-categories') .then(res => { if (!res.ok) throw new Error('请求失败'); return res.json(); }); } return allCategoriesCache; }, fetchByCategory: async (category: ArticleCategory) => { // 复用fetchAllCategories的缓存结果 const allData = await ArticleAPI.fetchAllCategories(); return allData[category]; } };
这样不管是generateStaticParams还是页面组件调用fetchByCategory,都会复用同一份API请求结果,不会重复发起请求。
为什么不能直接传递额外数据?
generateStaticParams的返回值仅允许包含路由参数(即URL中的动态部分),Next.js不会将额外数据传递给页面组件。这是因为静态生成的页面需要与路由一一对应,额外数据会增加构建产物的复杂度,同时在运行时(如增量静态再生)无法保证数据的一致性。
内容的提问来源于stack exchange,提问作者Topsy
相关产品推荐
相关产品推荐

