在真实项目里,「如果每次切换环境都手动改代码,不仅低效,还极易把测试地址带到线上。Vue CLI 把「模式 + 环境变量」做成了一套约定大于配置的体系,只要理解规则,就能让同一份源码在任意环境自动作出正确的行为。
一、模式(mode)

Vue CLI 把「运行命令」抽象成三种默认模式:
development:对应vue-cli-service serveproduction:对应vue-cli-service buildtest:对应vue-cli-service test:unit
模式本身不携带任何变量,它只是约定文件名前缀。
当你执行:
bash
npm run serve # 实际等价于 vue-cli-service serve --mode development
npm run build:staging # 自定义命令,等价于 vue-cli-service build --mode staging
CLI 会按以下顺序寻找文件:
.env.[mode].local.env.[mode].env.local(永远被 git ignore).env
同名变量后者覆盖前者,因此你可以把公共值写在 .env,把敏感值写在 .env.local,把环境特有值写在 .env.staging,一条命令即可切换。
二、环境变量
- 只有
VUE_APP_开头的变量才会被打包
任何机器级环境变量(PATH、HOME ...)都会被忽略,避免污染前端运行时。
想让变量进 bundle,必须加前缀:
bash
# .env.staging
VUE_APP_API_BASE=https://staging.api.example.com
在代码里直接用:
js
axios.defaults.baseURL = process.env.VUE_APP_API_BASE
构建时 CLI 会把 process.env.VUE_APP_API_BASE 替换为字符串字面量,零运行时开销。
- 运行时不可动态修改
变量在 npm run build 那一刻就被写死,前端无法通过 process.env.XXX = 'new' 去改。
需要运行时可变配置?把变量写成 JSON 文件或接口返回,再在前端异步加载即可。
三、一个文件,一条命令,三种环境
假设我们要同时支持 dev / staging / prod:
bash
根目录
├─ .env # 公共配置
├─ .env.development # 本地开发
├─ .env.staging # 预发
├─ .env.production # 线上
└─ package.json
文件内容示例:
bash
# .env
VUE_APP_TITLE=MyApp
# .env.development
VUE_APP_API_BASE=http://localhost:3000
# .env.staging
VUE_APP_API_BASE=https://staging.api.example.com
# .env.production
VUE_APP_API_BASE=https://api.example.com
自定义脚本:
json
"scripts": {
"serve": "vue-cli-service serve",
"build:staging": "vue-cli-service build --mode staging",
"build": "vue-cli-service build"
}
执行:
bash
npm run build:staging
CLI 自动读取 .env.staging 与 .env 合并,输出包里只有 https://staging.api.example.com。
四、CI/CD 中的最佳实践
-
不把敏感密钥写进仓库
把
.env*.local加入.gitignore,在 CI 里用环境变量注入:
bash
echo $STAGING_KEY >> .env.staging.local
-
单一 Dockerfile,多阶段构建
通过
ARG MODE动态决定--mode,同一份镜像可在测试、预发、生产之间漂移。 -
可视化差异
在构建日志里打印
console.log('Build mode:', process.env.NODE_ENV, process.env.VUE_APP_API_BASE),一眼确认变量是否生效。
五、常见问题
-
变量名可以改前缀吗?
可以,在
vue.config.js里设置envPrefix: 'APP_'即可。 -
为什么本地
.env改了值不生效?vue-cli-service serve会缓存旧进程,重启 dev-server 或加--no-cache即可。 -
如何读取非 VUE_APP 变量?
在
vue.config.js用chainWebpack手动注入:
js
config.plugin('define').tap(args => {
args[0]['process.env.CUSTOM'] = JSON.stringify(process.env.CUSTOM)
return args
})