Dotenv源码阅读

无论是vue还是react项目,项目根目录下一般都会有.env文件,保存不同环境的变量配置。而Dotenv就是一个零依赖模块,可将 .env文件中的环境变量加载至process.env中。那么Dotenv是如何做到这一点的?我打开了项目里所使用的Dotenv源码。

版本对比

以下的Dotenv源码阅读基于8.2.0的版本,目前最新的版本是16.3.1,核心文件mian.js中,代码量从100行扩展到了300行。我阅读了两个版本的代码,16.3.1的代码里主要添加了对环境变量进行加密的功能,核心方法configparse的逻辑不变,但是添加了许多更细致的处理规范,能理解8.2.0的代码逻辑就足够明白Dotenv是如何运作的了。
Dotenv gitlub地址:https://github.com/motdotla/dotenv/tree/master

//16.3.1版本中main.js所导出的方法
module.exports.configDotenv = DotenvModule.configDotenv
module.exports._configVault = DotenvModule._configVault
module.exports._parseVault = DotenvModule._parseVault
module.exports.config = DotenvModule.config
module.exports.decrypt = DotenvModule.decrypt
module.exports.parse = DotenvModule.parse
module.exports.populate = DotenvModule.populate

//8.2.0版本中main.js所导出的方法
module.exports.config = config
module.exports.parse = parse

源码分析

其实在main.js中,作者已经备注了config和parse方法分别是做什么的。简单来说
1.parse方法负责将.env文件中KEY = VAL的内容转化成对象形式
2.config方法负责遍历该对象,将其填充到process.env上
在这里插入图片描述

parse方法

const NEWLINE = '\n'
const RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*(.*)?\s*$/
const RE_NEWLINES = /\\n/g
const NEWLINES_MATCH = /\n|\r|\r\n/

function parse (src /*: string | Buffer */, options /*: ?DotenvParseOptions */) /*: DotenvParseOutput */ {
  const debug = Boolean(options && options.debug)
  const obj = {}

  // 将.env变量通过换行符分隔
  src.toString().split(NEWLINES_MATCH).forEach(function (line, idx) {
    // 判断每一项是否符合KEY = VAL的标准
    const keyValueArr = line.match(RE_INI_KEY_VAL)
    if (keyValueArr != null) {
      const key = keyValueArr[1]
      // 下面几行代码主要是在判断等号后的val值是使用单引号还是双引号包裹
      // 然后将引号包裹的值去除首位的空格后取出
      let val = (keyValueArr[2] || '')
      const end = val.length - 1
      const isDoubleQuoted = val[0] === '"' && val[end] === '"'
      const isSingleQuoted = val[0] === "'" && val[end] === "'"
      if (isSingleQuoted || isDoubleQuoted) {
        val = val.substring(1, end)
        if (isDoubleQuoted) {
          val = val.replace(RE_NEWLINES, NEWLINE)
        }
      } else {
        val = val.trim()
      }
      // 将该项添加至对象中
      obj[key] = val
    } else if (debug) {
      log(`did not match key and value when parsing line ${idx + 1}: ${line}`)
    }
  })

  return obj
}

config方法

function config (options /*: ?DotenvConfigOptions */) /*: DotenvConfigOutput */ {
  // 读取当前目录下的.env文件
  let dotenvPath = path.resolve(process.cwd(), '.env')
  let encoding /*: string */ = 'utf8'
  let debug = false

  if (options) {
    // 如果设置了自定义环境变量的文件,那么优先使用该文件
    if (options.path != null) {
      dotenvPath = options.path
    }
    // 编码方式
    if (options.encoding != null) {
      encoding = options.encoding
    }
    // 是否debug
    if (options.debug != null) {
      debug = true
    }
  }

  try {
    // 调用fs.readFileSync读取.env文件,将读取的结果传入parse方法,得到对象
    const parsed = parse(fs.readFileSync(dotenvPath, { encoding }), { debug })
    // 遍历该对象
    Object.keys(parsed).forEach(function (key) {
      // 如果process.env不存在该值,那么将将值添加到process.env上
      if (!Object.prototype.hasOwnProperty.call(process.env, key)) {
        process.env[key] = parsed[key]
      } else if (debug) {
        log(`"${key}" is already defined in \`process.env\` and will not be overwritten`)
      }
    })

    return { parsed }
  } catch (e) {
    return { error: e }
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值