Vue CLI 环境变量和模式:让开发、测试、生产配置分开

先说问题:不要在业务代码里到处判断 NODE_ENV

最开始我习惯这样写:

const BASE_URL = process.env.NODE_ENV === 'development'
  ? 'http://localhost'
  : 'https://example.com'

只有开发和生产两个环境时,这段代码还能勉强工作。一旦项目增加测试、预发布或演示环境,就会出现两个麻烦:

  1. 同一类配置要在多个文件里重复判断;
  2. NODE_ENV 只能表达构建环境,不能表达“这是测试环境还是预发布环境”。

更清楚的做法是:用 mode 选择配置文件,用 NODE_ENV 表示当前构建工具处于什么类型的运行状态。

Vue CLI 会读取哪些环境文件

在项目根目录可以放置:

.env                # 所有模式都会加载
.env.local          # 所有模式都会加载,但通常不提交 Git
.env.[mode]         # 只在指定 mode 下加载
.env.[mode].local   # 只在指定 mode 下加载,通常不提交 Git

例如:

.env
.env.local
.env.development
.env.test
.env.production
.env.test.local

同名变量存在时,模式专属文件和 .local 文件通常用于覆盖通用配置。不同 Vue CLI 版本的加载细节应以项目当前版本文档为准,最重要的是不要把密钥提交到公共仓库。

一个实际的配置例子

当时本地开发需要访问局域网服务,所以在 .env.local 中写了:

VUE_APP_BUILD_MODE=development
VUE_APP_BASE_URL=http://172.16.6.132:8002/threemiju/

测试环境单独写在 .env.test:

VUE_APP_BUILD_MODE=test
VUE_APP_BASE_URL=https://test.example.com/threemiju/

生产环境写在 .env.production:

VUE_APP_BUILD_MODE=production
VUE_APP_BASE_URL=https://example.com/threemiju/

Vue CLI 暴露给客户端代码的变量需要使用 VUE_APP_ 前缀:

export const baseURL = process.env.VUE_APP_BASE_URL
export const buildMode = process.env.VUE_APP_BUILD_MODE

这个前缀不是权限控制。只要变量被打进前端 bundle,用户就能在浏览器里看到它。因此 API Key、数据库密码和其他真正的 secret 不能放在 VUE_APP_ 变量里。

在 package.json 中显式选择 mode

一个简单的脚本可以这样写:

{
  "scripts": {
    "serve": "vue-cli-service serve",
    "serve:test": "vue-cli-service serve --mode test",
    "build:test": "vue-cli-service build --mode test",
    "build:production": "vue-cli-service build --mode production"
  }
}

运行时:

npm run serve
npm run serve:test
npm run build:test
npm run build:production

--mode test 的作用是选择 .env.test,不是让业务代码自己去猜当前环境。构建命令本身仍然会影响 NODE_ENV:开发服务器通常是 development,生产构建通常是 production。不要仅仅为了区分测试环境,就在 .env.test 里盲目覆盖 NODE_ENV。

如果测试构建确实需要生产优化,应先确认当前 Vue CLI 版本的行为,再检查构建结果,而不是只根据是否生成了一个 app.js 判断代码分割是否生效。

mode 和 NODE_ENV 的区别

可以这样理解:

概念解决的问题示例
mode选择哪一组 .env.[mode] 配置development、test、production
NODE_ENV告诉 Vue CLI 当前运行/构建处于哪种优化状态development、production、test
自定义变量表达项目自己的业务环境VUE_APP_BUILD_MODE=test

如果业务只需要显示“当前是测试环境”,使用 VUE_APP_BUILD_MODE 更直观,不要把所有逻辑都绑在 NODE_ENV 上。

环境变量的使用场景

1. 请求地址

import axios from 'axios'

export const api = axios.create({
  baseURL: process.env.VUE_APP_BASE_URL,
  timeout: 10000
})

2. 页面显示构建环境

<template>
  <span :title="buildMode">{{ version }} · {{ buildMode }}</span>
</template>

<script>
export default {
  data() {
    return {
      version: '1.0.0',
      buildMode: process.env.VUE_APP_BUILD_MODE || process.env.NODE_ENV
    }
  }
}
</script>

构建时变量会被替换进静态资源,所以修改 .env 后需要重新启动开发服务器或重新执行构建,运行中的页面不会自动读取新文件。

.local 文件什么时候有用

.local 文件适合保存“只属于当前机器”的配置:

  • 本机局域网 API 地址;
  • 临时调试开关;
  • 不希望提交到仓库的开发参数;
  • 团队成员各自不同的本地配置。

建议在仓库中提交一个不含敏感值的示例:

.env.local.example

并在 .gitignore 中忽略真正的本地文件:

.env.local
.env.*.local

这次踩坑留下的检查清单

遇到环境配置不生效时,我现在会按这个顺序检查:

  1. 当前命令实际使用了哪个 --mode;
  2. 文件名是否是 .env.[mode],而不是写成其他格式;
  3. 客户端变量是否使用了 VUE_APP_ 前缀;
  4. 修改变量后是否重启了开发服务器;
  5. 生产构建时是否把敏感信息打进了 bundle;
  6. 构建产物里是否真的出现了预期的接口地址;
  7. 是否错误地把 mode 和 NODE_ENV 当成了同一个概念。

环境变量的价值不是让配置文件变多,而是让“配置选择”从业务代码中移出去。项目环境一多,越应该让命令、配置文件和构建产物之间的关系清清楚楚。

参考资料

可用性说明:本文发布于 2020 年 8 月,距今已超过五年。文中涉及的软件版本、接口、下载地址、命令参数和操作界面可能已经发生变化,部分方案在当前环境下可能失效。请结合官方最新文档核对后再操作,生产环境使用前务必先行验证。

版权声明: 本文首发于 指尖魔法屋-Vue CLI 环境变量和模式:让开发、测试、生产配置分开(https://blog.thinkmoon.cn/post/894-guide-environment-vue/) 转载或引用必须申明原指尖魔法屋来源及源地址!