阅读 687

VUE CLI3 结合 cross-env 配置环境变量(含环境变量源码解析)

着急写项目的同行们,写在前面拿走不谢。

package.json

{
    ...
    "scripts":{
        "serve:dev": "cross-env API_ENV=dev vue-cli-service serve"
        ...
    },
    "dependencies": {
      ...
      "cross-env": "^7.0.2",
      ...
    },
    ...
}
复制代码

.env

VUE_APP_API_ENV=${API_ENV}
复制代码

项目中取用

console.log(process.env.VUE_APP_API_ENV)
复制代码

源码解析

vue cli3 中 环境变量和模式 写到它可以通过获取.env.env.local.env.[mode].env.[mode].local文件中的内容定义环境变量。

比如:

VUE_APP_TITLE=TITLE
复制代码

文档中写道:只有以 VUE_APP_ 开头的变量会被 webpack.DefinePlugin 静态嵌入到客户端侧的包中。

那我们来看源码:

base.js

引入方法resolveClientEnv.js,通过 webpack-chain, 链式配置了DefinePlugin使变量静态嵌入到客户端侧的包中。

 const resolveClientEnv = require('../util/resolveClientEnv')
    webpackConfig
      .plugin('define')
        .use(require('webpack').DefinePlugin, [
          resolveClientEnv(options)
        ])
复制代码

resolveClientEnv.js

就是这个方法定义了环境变量必须为VUE_APP_开始的规则,那么问题来了process.env里的值是哪里来的,为什么我们定义在配置文件中的值会被写入到process.env里接着往下看。


// 正则匹配以 VUE_APP_ 开头的 key
const prefixRE = /^VUE_APP_/

module.exports = function resolveClientEnv (options, raw) {
  const env = {}
  
  // 循环 process.env 的 key
  Object.keys(process.env).forEach(key => {
  
    // 匹配key符合正则或key等于NODE_ENV
    if (prefixRE.test(key) || key === 'NODE_ENV') {
      env[key] = process.env[key]
    }
  })
  env.BASE_URL = options.publicPath

  if (raw) {
    return env
  }

  for (const key in env) {
    env[key] = JSON.stringify(env[key])
  }
  
  // 返回环境变量配置
  return {
    'process.env': env
  }
}
复制代码

Service.js

从配置文件中获取环境变量的相关代码其中关键的是dotenvdotenv-expand

dotenv:将配置文件中的参数读取并写入环境变量

dotenv-expand : 将dotenv读取的环境变量进行再次处理筛选其中以${key}定义的变量,并查询node的环境变量中是否包含对应的key假如存在就赋值。

具体的代码我就不放了想看可以点上面的链接自己看。

const dotenv = require('dotenv')
const dotenvExpand = require('dotenv-expand')
...
module.exports = class Service {

  ...

  // 加载环境变量
  loadEnv (mode) {
    const logger = debug('vue:env')
    const basePath = path.resolve(this.context, `.env${mode ? `.${mode}` : ``}`)
    const localPath = `${basePath}.local`

    const load = envPath => {
      try {
        const env = dotenv.config({ path: envPath, debug: process.env.DEBUG })
        dotenvExpand(env)
        logger(envPath, env)
      } catch (err) {
        // only ignore error if file is not found
        if (err.toString().indexOf('ENOENT') < 0) {
          error(err)
        }
      }
    }

    load(localPath)
    load(basePath)

    // by default, NODE_ENV and BABEL_ENV are set to "development" unless mode
    // is production or test. However the value in .env files will take higher
    // priority.
    if (mode) {
      // always set NODE_ENV during tests
      // as that is necessary for tests to not be affected by each other
      const shouldForceDefaultEnv = (
        process.env.VUE_CLI_TEST &&
        !process.env.VUE_CLI_TEST_TESTING_ENV
      )
      const defaultNodeEnv = (mode === 'production' || mode === 'test')
        ? mode
        : 'development'
      if (shouldForceDefaultEnv || process.env.NODE_ENV == null) {
        process.env.NODE_ENV = defaultNodeEnv
      }
      if (shouldForceDefaultEnv || process.env.BABEL_ENV == null) {
        process.env.BABEL_ENV = defaultNodeEnv
      }
    }
  }
  
  ...
 
 }
复制代码

综上所述:我们就可以这样定义我们的环境变量

package.json

{
    ...
    "scripts":{
        "serve:dev": "cross-env API_ENV=dev vue-cli-service serve"
        ...
    },
    "dependencies": {
      ...
      "cross-env": "^7.0.2",
      ...
    },
    ...
}
复制代码

.env

VUE_APP_API_ENV=${API_ENV}
复制代码

项目中取用

console.log(process.env.VUE_APP_API_ENV)
复制代码

那么有人就会问了,这个在实际项目中的意义是什么?

假如公司有个项目共有三个环境:测试、预发、线上,而接口地址是需要通过环境变量判断的。并且我们希望在开发的过程中也可以调试线上或其他的服务接口且发布到线上的项目希望modeproduction,本地调试的项目我们希望modedevelopment那我得新建多少配置文件?