背景:之前在cli编译方式下,一直使用.env的方式进行环境变量的加载,非常的方便,而且在部署上也是把环境文件与开发人员分离开的。但是在使用HBuilderX的方式部署时,.env文件是加载不到的,需要重新思考和处理这个环境变量的问题。刚开始,我尝试使用了js做判断,在我看来,不算是个好方法,因为要在main.js中调用,在我看来在编译时使用最佳。所以,自己尝试了一下,也希望能分享和交流。
一、环境变量与编译方式
1.CLI编译
也就是:vue-cli-service的打包方式
我的理解:内核还是webpack,但是额外增加了一些处理,尤其是环境变量的处理。
注意:vue-cli-service只有三种环境,我之前以为可以自定义,其实不可以。
参考官方文档:https://cli.vuejs.org/zh/guide/mode-and-env.html#环境变量
这里明确说了:
想要了解解析环境文件规则的细节,请参考 dotenv。
vue-cli-service使用dotenv解析.env文件之后,使用@vue/cli-service/lib/util/resolveClientEnv.js
const prefixRE = /^VUE_APP_/  
module.exports = function resolveClientEnv (options, raw) {  
  const env = {}  
  Object.keys(process.env).forEach(key => {  
    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  
  }  
}  
过滤了一部分key,不是APP_VUE*这个开头的全局变量就会被过滤掉,此外还保留了NODE_ENV
通常来说,我们都是在项目的根目录下面执行vue-cli-service,所以默认的环境变量文件.env也是在根目录下面。
2.HBuilderX编译
HBuilderX的编译时,也是使用的webpack。但是HBuilderX的编译,是没有处理根目录下的.env文件的。
二、解决方案
1.使HBuilderX也能自动加载.env文件
在根目录下的vue.config.js(如果没有就创建一个)
const webpack = require('webpack')  
const dotenv = require('dotenv')  
module.exports = {  
  chainWebpack: config => {  
    config  
      .plugin('define')  
      .tap(args => {  
        //console.log(args)  
        const config = getEnvsByDot()  
        //console.log(config)  
        // 将自定义的环境变量塞入配置中  
                //这里有个格式转化时的字符串的坑  
        //'"http://127.0.0.1:8088/"' //这种没问题  
        //'http://127.0.0.1:8088/' //这种就报错了!  
        Object.keys(config).forEach(key => {  
            if(typeof config[key] == "string"){  
                config[key] = '"'+config[key]+'"'  
            }  
            args[0]['process.env'][key] = config[key]  
        })  
        //args[0]['process.env'] = Object.assign(args[0]['process.env'],config)  
        console.log(args)  
        return args  
      })  
  }  
}  
/**  
 * 从.env中获取  
 * 使用了dotenv依赖  
 * 模仿vue-cli-service,不是APP_VUE*这个开头的全局变量就会被过滤掉  
 */  
function getEnvsByDot(){  
    const prefixRE = /^VUE_APP_/  
    let dotEnvs = {}  
    //1.先加载通用环境变量  
    const envPath0 = __dirname+'/.env'  
    const dotEnvsConfig0 = dotenv.config({path:envPath0})  
    console.log(dotEnvsConfig0)  
    if(!dotEnvsConfig0.error){  
        Object.keys(dotEnvsConfig0.parsed).forEach(key => {  
          if (prefixRE.test(key) ) {  
            dotEnvs[key] = dotEnvsConfig0.parsed[key]  
          }  
        })  
    }  
    //2.再加载专属环境变量  
    let env='local'  
    if (process.env.NODE_ENV === 'development') {  
        env='dev'  
    } else if (process.env.NODE_ENV === 'production') {  
        env='prod'  
    }else{  
        env=process.env.NODE_ENV  
    }  
    const envPath = __dirname+'/.env.'+env  
    const dotEnvsConfig = dotenv.config({path:envPath})  
    console.log(dotEnvsConfig)  
    if(!dotEnvsConfig.error){  
        Object.keys(dotEnvsConfig.parsed).forEach(key => {  
          if (prefixRE.test(key) ) {  
            dotEnvs[key] = dotEnvsConfig.parsed[key]  
          }  
        })  
    }  
    return dotEnvs  
}
当然,你也可以不过滤VUE_APP_前缀,自由的使用环境变量,自己注意不要产生与系统自带变量名的冲突就好了。
这个方法,应该足够让.env文件在HBuilderX下的使用与cli一样舒服了。但也带来一个问题,如果你这个项目同时使用cli编译的话,可能会导致.env被加载两次。当然了一般来说,由于两种方式创建的项目的文件夹结构不一样,所以可能你不会遇到既是用cli又是用HBuilderX编译的情况。  
2.用yaml文件编译
其实yaml文件类型这几年在配置文件上用得挺多的,好读好写。
同样是在根目录下的vue.config.js(如果没有就创建一个)
const webpack = require('webpack')  
const YAML = require('yaml')  
const fs = require('fs')  
module.exports = {  
  chainWebpack: config => {  
    config  
      .plugin('define')  
      .tap(args => {  
        console.log(args)  
        const config = getEnvsByYaml()  
        console.log(config)  
        // 将自定义的环境变量塞入配置中  
        args[0]['process.env'] = Object.assign(args[0]['process.env'],config)  
        console.log(args)  
        return args  
      })  
  }  
}  
/**  
 * 从env文件中读取配置:  
 * 1.要获取文件绝对路径,因为相对路径是相对于编译器的,而不是项目的根目录。使用了__dirname。  
 * 2.使用了yaml格式。  
 */  
function getEnvsByYaml(){  
    //1.先加载通用环境变量  
    const prefixRE = /^VUE_APP_/  
    let yamlEnvs = {}  
    let config0 = {}  
    try{  
        const envYaml0 = fs.readFileSync(__dirname+'/.env.yaml','utf8')  
        config0 = YAML.parse(envYaml0)  
    }catch(e){  
        console.log(e)  
    }  
    // console.log(config0)  
    Object.keys(config0).forEach(key => {  
      if (prefixRE.test(key) ) {  
        yamlEnvs[key] = config0[key]  
      }  
    })  
    //2.再加载专属环境变量  
    let env='local'  
    if (process.env.NODE_ENV === 'development') {  
        env='dev'  
    } else if (process.env.NODE_ENV === 'production') {  
        env='prod'  
    }else{  
        env=process.env.NODE_ENV  
    }  
    let config = {}  
    try{  
        const envYaml = fs.readFileSync(__dirname+'/.env.'+env+'.yaml','utf8')  
        config = YAML.parse(envYaml)  
    }catch(e){  
        console.log(e)  
    }  
    // console.log(config)  
    Object.keys(config).forEach(key => {  
      if (prefixRE.test(key) ) {  
        yamlEnvs[key] = config[key]  
      }  
    })  
    return yamlEnvs  
}
3.同时使用.env和yaml
配置好,爱用哪个就用哪个,也可以同时用,但要确定好优先级。我个人会把yaml的优先级放高一点,因为它更结构化。
方法就是把上面两个的代码一次执行,先下载.env,再加载yaml
            
            
            
            
10 个评论
要回复文章请先登录或注册
1***@qq.com
l***@163.com
MoreBetter
董路飞
w***@163.com
paddyP
Leo星星 (作者)
2***@qq.com
嘻嘻哈哈S
1***@qq.com