You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 09:15:35