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

Next.js项目构建后如何从服务端向客户端传递动态API地址(基于环境变量)

Next.js项目构建后如何从服务端向客户端传递动态API地址(基于环境变量)

这个需求我太熟悉了——一次构建、多实例部署,每个实例靠环境变量切换API地址,核心就是要让客户端能在运行时动态获取服务端的环境变量值,而不是把值固化在构建产物里。我给你几个经过验证的可行方案,你可以根据自己的场景选:

方案一:在服务端渲染时把配置注入全局window对象

这是最直接的方式,不需要额外的网络请求,客户端加载页面时就能拿到配置。

你可以修改pages/_document.js(如果用App Router的话是app/layout.js服务端组件),在服务端获取环境变量,然后把它注入到HTML的全局脚本里:

代码示例(Pages Router):

// pages/_document.js
import Document, { Html, Head, Main, NextScript } from 'next/document';

class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx);
    // 服务端获取环境变量,映射成对应的API地址
    const customerApiUrl = process.env.CUSTOMER === 'customerA' 
      ? 'https://api.customerA.com' 
      : process.env.CUSTOMER === 'customerB'
      ? 'https://api.customerB.com'
      : 'https://api.default.com';
    
    return { ...initialProps, customerApiUrl };
  }

  render() {
    return (
      <Html>
        <Head />
        <body>
          {/* 把API地址注入全局window对象 */}
          <script
            dangerouslySetInnerHTML={{
              __html: `window.APP_CONFIG = { apiUrl: "${this.props.customerApiUrl}" };`,
            }}
          />
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default MyDocument;

代码示例(App Router):

// app/layout.js
export default function RootLayout({ children }) {
  // 服务端组件可以直接访问process.env
  const customerApiUrl = process.env.CUSTOMER === 'customerA' 
    ? 'https://api.customerA.com' 
    : process.env.CUSTOMER === 'customerB'
    ? 'https://api.customerB.com'
    : 'https://api.default.com';

  return (
    <html lang="en">
      <body>
        <script
          dangerouslySetInnerHTML={{
            __html: `window.APP_CONFIG = { apiUrl: "${customerApiUrl}" };`,
          }}
        />
        {children}
      </body>
    </html>
  );
}

之后客户端任何组件里都可以直接用window.APP_CONFIG.apiUrl获取API地址。这个方案的优势:

  • 无需额外请求,页面加载时就拿到配置
  • 每次启动服务时环境变量变化,值会自动更新(因为是服务端渲染时动态生成的)

方案二:用Next.js API路由作为配置中间层

这个方案适合需要更灵活配置的场景,比如后续要加更多配置项,或者需要对配置请求做权限验证。

核心思路是:客户端通过相对路径请求自己部署的API路由,这个路由在服务端读取环境变量后返回对应API地址。因为是相对路径,客户端完全不需要知道自己的部署域名——浏览器会自动用当前页面域名发起请求。

步骤:

  1. 创建API路由文件:
// Pages Router: pages/api/get-config.js
export default function handler(req, res) {
  // 服务端读取环境变量,映射API地址
  const customerApiUrl = process.env.CUSTOMER === 'customerA' 
    ? 'https://api.customerA.com' 
    : process.env.CUSTOMER === 'customerB'
    ? 'https://api.customerB.com'
    : 'https://api.default.com';

  res.status(200).json({ apiUrl: customerApiUrl });
}

// App Router: app/api/get-config/route.js
export async function GET() {
  const customerApiUrl = process.env.CUSTOMER === 'customerA' 
    ? 'https://api.customerA.com' 
    : process.env.CUSTOMER === 'customerB'
    ? 'https://api.customerB.com'
    : 'https://api.default.com';

  return Response.json({ apiUrl: customerApiUrl });
}
  1. 客户端组件里请求这个路由:
// 客户端组件
import { useEffect, useState } from 'react';

function MyComponent() {
  const [apiUrl, setApiUrl] = useState('');

  useEffect(() => {
    async function fetchConfig() {
      const res = await fetch('/api/get-config');
      const config = await res.json();
      setApiUrl(config.apiUrl);
    }
    fetchConfig();
  }, []);

  return <div>当前API地址:{apiUrl}</div>;
}

export default MyComponent;

这个方案的优势:

  • 完全解耦客户端和服务端的配置逻辑,后续修改配置规则只需要改API路由
  • 支持复杂场景,比如根据请求头返回不同配置,或者加缓存

方案三:启动前生成静态配置文件

如果你不想用服务端渲染或者API路由,可以在启动Next.js之前,根据环境变量生成一个静态配置文件放到public目录,客户端直接请求这个静态文件。

步骤:

  1. 写一个生成配置的脚本generate-config.js:
// generate-config.js
const fs = require('fs');
const path = require('path');

// 根据环境变量映射API地址
const customerApiUrl = process.env.CUSTOMER === 'customerA' 
  ? 'https://api.customerA.com' 
  : process.env.CUSTOMER === 'customerB'
  ? 'https://api.customerB.com'
  : 'https://api.default.com';

// 生成config.json到public目录
const configContent = JSON.stringify({ apiUrl: customerApiUrl });
fs.writeFileSync(path.join(__dirname, 'public', 'config.json'), configContent);

console.log('配置文件生成成功,API地址:', customerApiUrl);
  1. 修改package.json的启动脚本,先运行生成脚本再启动Next.js:
{
  "scripts": {
    "start": "node generate-config.js && next start",
    "build": "next build"
  }
}
  1. 客户端组件里请求这个静态文件:
// 客户端组件
import { useEffect, useState } from 'react';

function MyComponent() {
  const [apiUrl, setApiUrl] = useState('');

  useEffect(() => {
    async function fetchConfig() {
      const res = await fetch('/config.json');
      const config = await res.json();
      setApiUrl(config.apiUrl);
    }
    fetchConfig();
  }, []);

  return <div>当前API地址:{apiUrl}</div>;
}

export default MyComponent;

这个方案的优势是简单易懂,适合不需要复杂逻辑的场景,但要注意每次启动服务前都要运行生成脚本,部署时要确保启动命令包含这一步。

为什么你之前的尝试没成功?

  • PUBLIC_NEXT_前缀的变量:Next.js会在构建时把这些变量注入客户端代码,构建后再改环境变量没用——值已经固化在JS文件里了。
  • next.config.js里的env配置:同样是构建时处理的,构建后修改环境变量不会更新客户端拿到的值。
  • 读取.txt文件:客户端无法直接读取服务端的文件,只能通过服务端接口或者静态资源的方式获取。

备注:内容来源于stack exchange,提问作者Vlad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 09:39:32