Next.js构建时如何实现公共API单次调用全页面复用?
Next.js构建阶段复用公共API请求的实现方案
针对Pages Router + ISR的场景,用Node.js模块级单例缓存就能零依赖解决问题,不需要引入额外工具,构建阶段三个公共接口只会触发1次请求,完全不影响ISR运行时的增量更新逻辑。
核心原理
Next.js执行生产构建时是常驻Node进程,所有引入的模块遵循CommonJS/ESM单例规则,只要在公共模块里声明变量存储接口响应,所有页面的getStaticProps执行时都能读取到同一份缓存数据,不会重复发起请求。
具体实现
1. 封装公共数据拉取方法
新建工具文件存放缓存逻辑,同时处理并发请求、构建/运行时环境区分的边界问题:
// lib/common-build-data.ts import { PHASE_PRODUCTION_BUILD } from 'next/constants'; // 模块级缓存,构建全程仅存一份数据 type CommonData = { headerContent: Record<string, any>; footerContent: Record<string, any>; siteConfig: Record<string, any>; }; let cachedData: CommonData | null = null; let pendingRequest: Promise<CommonData> | null = null; async function fetchCommonData(): Promise<CommonData> { // 并行拉取三个公共接口,减少等待时间 const [headerRes, footerRes, configRes] = await Promise.all([ fetch(`${process.env.CMS_ENDPOINT}/layout/header`), fetch(`${process.env.CMS_ENDPOINT}/layout/footer`), fetch(`${process.env.CMS_ENDPOINT}/config/site`), ]); const [headerContent, footerContent, siteConfig] = await Promise.all([ headerRes.json(), footerRes.json(), configRes.json(), ]); return { headerContent, footerContent, siteConfig }; } export async function getCommonData() { // 非构建阶段(开发环境、ISR运行时重生成)直接拉最新数据,不用缓存 if (process.env.NEXT_PHASE !== PHASE_PRODUCTION_BUILD) { return fetchCommonData(); } // 已有缓存直接返回 if (cachedData) return cachedData; // 已有正在执行的请求,直接复用Promise,避免并发场景重复发请求 if (pendingRequest) return pendingRequest; pendingRequest = (async () => { const data = await fetchCommonData(); cachedData = data; pendingRequest = null; return data; })(); return pendingRequest; }
### 2. 页面中替换原有重复请求
所有页面的getStaticProps里不再单独写三个公共接口的请求逻辑,直接调用封装好的方法即可:
// pages/[slug].tsx 示例 import { getCommonData } from '@/lib/common-build-data'; export async function getStaticProps({ params }) { // 构建阶段第一次调用会发请求,后续所有页面调用直接读缓存 const commonData = await getCommonData(); // 拉取当前页面独有的业务数据 const pageContent = await fetch(`${process.env.CMS_ENDPOINT}/page/${params.slug}`).then(res => res.json()); return { props: { ...commonData, pageContent, }, // ISR配置正常保留即可,运行时重生成会自动拉取最新的公共数据 revalidate: 60, }; } // 页面组件正常接收props渲染即可 export default function Page({ headerContent, footerContent, siteConfig, pageContent }) { // 页面渲染逻辑 }
注意事项
- 不要用Redux、React Context这类客户端状态方案存储构建期公共数据:这类方案仅在客户端/单个页面渲染周期生效,无法在
getStaticProps跨页面共享状态,完全起不到缓存作用。 - 环境判断逻辑必须保留:否则自托管场景下Node进程长驻,会导致ISR运行时重生成页面一直用构建时的旧公共数据,无法更新。
- 公共接口请求用
Promise.all并行发起:比串行请求速度快2/3,进一步压缩构建时间。
如果你的页面量特别大,不想每个页面都手动透传公共数据,可以配合自定义_document.tsx注入全局数据,适合纯展示类的CMS站点。
内容的提问来源于stack exchange,提问作者Parvesh Goyal
相关产品推荐
相关产品推荐

