Vite 生产构建后 import.meta.env 变 undefined?原因与修复
导语:vite build 之后 import.meta.env.VITE_SOMETHING 返回 undefined,是 Vite 社区最常见的疑问之一。本文译自 dev.to 上的一篇排查指南,梳理了四种典型成因与对应修复方式。原文链接见文末。
问题现象
一个用 Vite 构建的 React 应用从环境变量读取 API 地址,在 vite dev 下工作正常,部署后却变成空值:
const url = import.meta.env.VITE_SERVER_URL; // 构建后为 undefined在 DevTools 中打开编译后的 bundle,可以看到整个 import.meta.env 对象存在,但需要的那个键缺失或为空。原始提问的代码读取了 VITE_SERVER_URL、VITE_API_ENDPOINT、VITE_AUTH_ENDPOINT 三个变量,并用 import.meta.env.MODE 区分生产与开发环境,同时用 console.log(url, api, auth) 调试。三个变量在 vite dev 下都正常,只有构建产物丢失了它们,而 Vite 的构建过程不会给出任何警告或错误。
根本原因
Vite 官方文档写得很直接:只有以 VITE_ 为前缀的变量,才会在打包后被暴露到客户端源码中。其他任何环境变量——API_KEY、SERVER_URL、DATABASE_URL——都会被刻意排除在构建产物的 import.meta.env 之外。这不是疏漏,而是防止 .env 文件中同时存放的服务端密钥泄露到任何人都能阅读的 JavaScript bundle 里。
三种常见错误会造成同样的症状:
- 缺少
VITE_前缀。.env中的SERVER_URL=https://api.example.com对import.meta.env不可见,只有VITE_SERVER_URL=...才会被暴露。 .env文件不在项目根目录。Vite 相对于其环境目录(默认为项目根目录)查找.env文件,放在src/里的文件永远不会被读取。- 在浏览器代码中读取
process.env。一些旧教程和 CRA 时代的代码使用process.env.*。Vite 不会像 Create React App 那样为客户端代码 polyfillprocess.env,因此该写法对任何未单独定义的变量都会静默返回undefined。
原始提问中开发与生产的差异还有第四个原因:在 vite.config.ts 中手动执行 process.env = { ...process.env, ...loadEnv(mode, process.cwd()) }。这一行只在 Vite 配置求值期间修补了 Node 的 process.env,对最终进入客户端 bundle 的 import.meta.env 毫无影响。因此它可能在某些开发场景下看起来"有效",但实际应用代码在构建产物中永远读不到这些值。
修复方式
1. 加上 VITE_ 前缀(默认修复)
# 修改前
SERVER_URL=https://api.example.com
# 修改后
VITE_SERVER_URL=https://api.example.comconst url = import.meta.env.VITE_SERVER_URL;重新构建即可,无需改动 vite.config。这覆盖了绝大多数此类报错。
2. 用 envPrefix 自定义前缀
如果逐个重命名不现实,Vite 允许修改它查找的前缀:
import { defineConfig } from 'vite';
export default defineConfig({
envPrefix: 'APP_', // 现在暴露 APP_* 而非 VITE_*
});Vite 会直接拒绝将 envPrefix 设为空字符串,因为那会暴露所有环境变量——包括从未打算进入浏览器的那些。
3. 确认 .env 文件位置与构建模式
确保文件位于项目根目录、与 vite.config.ts 同级,而不是在 src/ 内,并且与 Vite 的构建模式匹配:
.env # 所有模式都会加载
.env.production # 仅 `vite build` 加载
.env.development # 仅 `vite dev` 加载如果需要在 vite.config.ts 内部显式加载环境变量——例如通过 define 注入值,而不是依赖自动的 import.meta.env 暴露——可以使用 loadEnv 的第三个参数绕过前缀过滤:
import { defineConfig, loadEnv } from 'vite';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), ''); // '' = 忽略前缀,仅限配置期
return {
define: {
__APP_VERSION__: JSON.stringify(env.npm_package_version),
},
};
});这一模式只服务于配置文件在构建时需要的值,它仍然不会把无前缀变量放进应用代码的 import.meta.env,后者是另一条有意为之的限制。
4. 停止在浏览器代码中读取 process.env
如果代码读取的是 process.env.VITE_SERVER_URL 而非 import.meta.env.VITE_SERVER_URL——这在从 Create React App 迁移的项目中很常见——Vite 不会做同样的填充。Vite 的客户端 bundle 默认没有 process.env polyfill,因此浏览器运行代码中对它的任何引用都会返回 undefined,无论前缀、构建模式或 .env 位置如何。在代码库中搜索 process.env.VITE_,把每一处替换为 import.meta.env.VITE_,这是机械的查找替换,不是配置改动。
验证修复
在本地构建并预览生产包,不要只信任 dev 模式:
npm run build
npm run preview打开应用,检查网络面板,或临时在入口文件加一句 console.log(import.meta.env),确认带 VITE_ 前缀的键现在持有预期值而非 undefined。如果在 CI 流水线中调试,还要检查 CI 任务中 vite build 步骤的日志:CI 检出时缺少 .env.production 文件(而开发者本机可能存在未纳入版本控制的该文件)会复现完全相同的症状,且如果只在本地测试过构建,很容易忽略这一点。
Docker 会改变 .env 文件的位置要求
如果变量前缀正确,但在 Docker 构建的镜像中仍然为空,通常原因不同:.env 文件在 vite build 运行前没有被复制进构建上下文,或者该变量本应作为构建参数(Dockerfile 中的 ARG / ENV)注入,而不是从只存在于宿主机的文件读取。这是构建上下文问题,不是 Vite 前缀问题,需要在镜像构建时显式传入该值。
原文链接
https://dev.to/mahdi_benrhouma_fe1c6005/fix-importmetaenv-undefined-on-production-build-vite-5d51