dotenv在Nx与Vite中的冲突问题
Nx 预加载环境变量导致 Vite 本地 env 覆盖失效的问题分析与解决方案
[!info] related notes
dotenv在Nx与Vite中的冲突问题
这是一个非常经典的 Nx Monorepo 与 Vite 环境变量加载机制冲突的问题!
你的直觉完全正确:按照 Vite 的官方文档,.env.development.local 的优先级理应高于 .env。
但为什么现在表现出来的却是 .env 优先级最高了呢?罪魁祸首是 Nx(你的 Monorepo 构建工具)。
下面我为你揭秘这个“幽灵覆盖”是怎么发生的:
🔍 为什么 .env 反而“覆盖”了 .local?
程序的启动顺序其实分为两步:先启动 Nx,再由 Nx 启动 Vite。
- 第一步:Nx 启动并抢先加载
.env**当你运行nx serve web或者通过 Nx 运行任何命令时,Nx 会在整个进程的最开始,自动去读取根目录下的基础.env文件。 它读取到VITE_ENABLE_MOCK_API=false后,会直接把这个值塞进 Node.js 系统的最高级内存——process.env** 里面。 - **第二步:Vite 启动并执行
loadEnv**接着 Vite 开始工作,它去乖乖地寻找.env.development.local,并且也成功找到了里面的VITE_ENABLE_MOCK_API=true。 但是! Vite 有一个铁打的规则:系统环境变量(process.env)的优先级绝对高于任何配置文件。 Vite 在合并时发现:“咦?process.env里已经有一个VITE_ENABLE_MOCK_API=false了(其实是 Nx 刚才塞进去的),那我绝对不能用文件里的值去覆盖系统的原配值!”
结论: 并不是 Vite 觉得 .env 的优先级比 .env.development.local 高,而是 Nx 把 .env 里的值变成了系统的 process.env**,从而产生了降维打击**,导致你后面所有的 .local 和 .development 文件中同名的变量全部失效。
当你把 .env 里的值改成 true 时,Nx 塞给 process.env 的就是 true,所以它恢复了。
🛠️ 如何优雅地解决这个问题?
为了让不同环境的配置文件(如 .local)能够正常发挥它们“覆盖”的作用,我们通常建议采用以下几种做法:
方法一:把特定变量从基础 .env 中移除(最推荐)
基础的 .env 文件只应该放全环境通用的配置(比如项目名称 APP_NAME=DailyUse)。
像 VITE_ENABLE_MOCK_API 这种在不同环境(本地、开发、生产)经常要变的值,不要写在最外层的 .env 里。
- 请把它从
.env中删掉。 - 默认的
false可以写在.env.production里。 - 默认的
true可以写在.env.development里。 - 本地想关掉,就在
.env.development.local写false。 这样 Nx 启动时就不会把这个变量锁死在process.env里,Vite 就能完美接管环境文件优先级的合并了。
方法二:在 Nx 的 project.json 中指定 env 文件(实测没有用,还是以 .env 为准)
[!notice] (实测没有用,还是以 .env 为准) AI 给的回答,虽然方法也是 AI 给的。 Nx CLI 极其“勤快”:当你敲下
nx serve回车的那一瞬间,Nx 的核心程序启动,它会无条件、最优先地去读取你根目录下的.env文件,并把里面的值全部塞进 Node 的process.env内存里。
如果你一定要保留在 .env 中,你可以告诉 Nx 在运行 serve 时优先加载你的 local 文件。
打开 apps/web/project.json,找到 serve 的 options,配置 envFile:
"targets": {
"serve": {
"executor": "@nx/vite:dev-server",
"defaultConfiguration": "development",
"options": {
"buildTarget": "web:build",
// 👇 强制 Nx 优先读取这个文件塞入 process.env
"envFile": ".env.development.local"
}
}
}
小提示(防低级错误)
你在提问中打字写的是 .env.develop.local,请务必确认你实际的文件名是 .env.development.local,少了一个 ment 的话,Vite 也是绝对不会去读取它的哦!