JavaScript is required
Back

💡 解决 Vite 配置中 process.env 读取不到环境变量的问题

2025/07/17

✨ 前言

在使用 Vite 进行前端开发时,我们经常需要读取 .env 文件中的环境变量配置,例如 API 地址、端口、静态资源路径等。最近我在项目中遇到一个问题:

  • ✅ 配置 A 可以正常使用
  • ❌ 配置 B 无法正确读取环境变量
  • 下面记录一下排查过程与原因分析,帮助自己加深理解,也方便后来者。

✅ 可正常使用的配置

import { createServer, defineConfig, loadEnv } from "vite";

export default defineConfig(({ mode }) => {
  const { VITE_HTML_DIR, VITE_API_URL, VITE_PORT } = loadEnv(
    mode,
    process.cwd()
  );
  console.log(VITE_HTML_DIR, VITE_API_URL, VITE_PORT);
  return {
    root: VITE_HTML_DIR || "html",
    server: {
      proxy: {
        "^/ajax": {
          target: VITE_API_URL,
          changeOrigin: true,
          rewrite: (path) => path.replace("/ajax", ""),
        },
      },
      port: VITE_PORT || 9090,
      host: "0.0.0.0",
      before: (app) => {
        app.use("/", createServer({}).middlewares);
      },
    },
  };
});
🔍 特点
  • 使用了 defineConfig(({ mode }) => {...}) 函数式配置。
  • 使用 loadEnv 加载 .env 文件并获取变量。

❌ 无法使用的配置

import { createServer, defineConfig } from "vite";
import dotenv from "dotenv";

dotenv.config();

export default defineConfig({
  root: process.env.VITE_HTML_DIR || "html",
  server: {
    proxy: {
      "^/ajax": {
        target: process.env.VITE_API_URL,
        changeOrigin: true,
        rewrite: (path) => path.replace("/ajax", ""),
      },
    },
    port: process.env.VITE_PORT || 9090,
    host: "0.0.0.0",
    before: (app) => {
      app.use("/", createServer({}).middlewares);
    },
  },
});
🔍 特点
  • 使用对象式配置 defineConfig({ ... })。
  • 使用 dotenv.config() 加载 .env 文件,直接读取 process.env.VITE_API_URL。

🧐 问题表现

在第二份配置中,process.env.VITE_API_URL 读取到的值为 undefined,导致 Vite proxy 无法代理到目标 API,接口全部 404。


💡 问题原因

1. dotenv.config() 和 loadEnv 的区别
  • dotenv.config() 仅加载根目录下的 .env 文件,不会加载 .env.development、.env.production 之类的文件。
  • Vite 提供的 loadEnv(mode, root) 函数,会根据 mode(如 development / production)加载:
    • .env
    • .env.local
    • .env.mode
    • .env.mode.local

并返回合并后的对象。


2. loadEnv 不会自动注入到 process.env

调用 loadEnv 返回的环境变量需要手动使用,例如:

const env = loadEnv(mode, process.cwd());
console.log(env.VITE_API_URL);

如果希望它写入 process.env,需要:

Object.assign(process.env, env);

3. 函数式配置 vs 对象式配置
  • 函数式配置可以拿到 mode 参数,方便调用 loadEnv。
  • 对象式配置没有 mode 参数,除非你通过 process.env.NODE_ENV 判断当前模式,但这在 Vite 启动流程中存在时序问题,容易拿不到正确 mode。

🔧 解决方案

✅ 方案 1. 使用函数式配置

最推荐,官方最佳实践。

import { createServer, defineConfig, loadEnv } from "vite";

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd());
  return {
    root: env.VITE_HTML_DIR || "html",
    server: {
      proxy: {
        "^/ajax": {
          target: env.VITE_API_URL,
          changeOrigin: true,
          rewrite: (path) => path.replace("/ajax", ""),
        },
      },
      port: env.VITE_PORT || 9090,
      host: "0.0.0.0",
      before: (app) => {
        app.use("/", createServer({}).middlewares);
      },
    },
  };
});
✅ 方案 2. 对象式配置时手动合并 env

如果必须使用对象式配置,可在文件顶部调用 loadEnv 并 merge 到 process.env:

import { createServer, defineConfig, loadEnv } from "vite";

const env = loadEnv(process.env.NODE_ENV || "development", process.cwd());
Object.assign(process.env, env);

export default defineConfig({
  root: process.env.VITE_HTML_DIR || "html",
  server: {
    proxy: {
      "^/ajax": {
        target: process.env.VITE_API_URL,
        changeOrigin: true,
        rewrite: (path) => path.replace("/ajax", ""),
      },
    },
    port: process.env.VITE_PORT || 9090,
    host: "0.0.0.0",
    before: (app) => {
      app.use("/", createServer({}).middlewares);
    },
  },
});
🎯 最佳实践
  • ✔️ 使用 函数式配置 并调用 loadEnv
  • ✔️ 避免混用 dotenv.config() 与 loadEnv
  • ✔️ 在需要多环境配置时,统一封装 getEnv() 工具函数

✏️ 总结

这次踩坑让我深入理解了:

  • Vite 环境变量的加载机制
  • loadEnv 与 dotenv 的本质区别
  • 配置函数式与对象式的应用场景

希望对你有所帮助。如果你在项目配置或多环境管理上有其他问题,欢迎留言一起讨论。