开发、测试、预发、生产环境各不相同:API 地址、密钥、调试开关都要换。
主流脚手架都用 .env 文件来解决这件事,它遵循
.env.[mode].[local] 的命名规则,加载时有严格的优先级与覆盖关系。
下面是这套机制的骨架与结论,完整的场景示例与陷阱清单请到掘金阅读。
一句话要点
- 优先级:
.env.[mode].local>.env.[mode]>.env.local>.env,后加载的覆盖先加载的。 - 客户端变量必须带前缀才会被注入:Vite 用
VITE_、CRA 用REACT_APP_、Vue CLI 用VUE_APP_。 - 变量替换发生在构建时,值是字符串;不要用它传任何真正的机密。
一、四个文件,一套规则
.env # 所有环境都会加载的公共变量
.env.local # 所有环境都会加载的本地私有变量(通常被 .gitignore 忽略)
.env.[mode] # 只在特定模式下加载,如 .env.development
.env.[mode].local # 只在特定模式加载的本地私有变量
mode 由运行时指定的 --mode 参数决定:
vite dev 默认 development,vite build 默认 production,
vite build --mode staging 就是自定义的 staging。
二、加载顺序与优先级
以 vite build --mode staging 为例,实际加载顺序是:
.env(公共基础).env.local(本地覆盖,被 git 忽略).env.staging(模式特定,可提交).env.staging.local(模式特定本地覆盖,被忽略)
团队共享的基础配置放.env或.env.[mode]并提交; 个人差异写进.local,两者互不干扰。
三、前缀是硬性规则
# 仅 VITE_ 开头的变量会被注入到客户端代码
VITE_API_BASE=http://localhost:3000/api
VITE_DEBUG=true
# 这个不会注入,只在 Node 端构建时可用
DB_HOST=localhost
注意 .env 里的值都是字符串,需要布尔或数字时务必显式转换,
比如 VITE_DEBUG === 'true'。
四、团队协作:怎么保证拉下来就能跑
这是最常见的痛点。解法是把变量明确分成两类:
- 公共配置(API 地址、功能开关、环境标识、Mock 开关)→ 写进
.env.development/.env.production并提交到仓库。 - 私密配置(第三方密钥、个人 Token)→ 放在
.env.local,不提交。
再提供一个不含真实值的 .env.example 并提交,新成员加入时的流程就变成:
克隆 → cp .env.example .env.local → 填自己的密钥 → npm run dev。
线上构建同理:CI/CD 环境不需要 .env.local 文件,
而是在平台的环境变量界面注入同名变量,构建时自动读取。
五、四个容易踩的坑
- 变量替换在构建时发生,无法在运行时动态读取整个
import.meta.env,必须写成具体字段。 - 忘记加前缀的变量不会被注入客户端,只能在 Node 侧访问。
- 带前缀的变量会直接打包进浏览器代码,任何人都能看到源码。
- 值都是字符串,布尔与数字要手动转换。
小结
这套机制优雅的地方在于:用文件名的层级表达配置的层级。
记住三件事就够了——按 --mode 加载、后加载覆盖先加载、客户端变量必须带前缀。