每天阅读一个npm 包源码:dotenv的作用,项目中.env文件是干嘛的?
点击上方 前端阳光,关注公众号
回复加群,加入技术交流群交流群

.env文件 的作用
💡 加载 .env 文件中的变量到 process.env 中
.env 文件是用来自定义配置的一个简单方法。我们可以将一些不能在代码中存储的 敏感信息 / 账号数据 从代码中剥离出来,作为环境变量存储在 .env 文件中。
.env 的使用方法
.env 文件通常不包含在版本控制内,因为它可能包含敏感的 API Key 或 密码。所有需要环境变量定义的项目都可以创建一个 .env.example 文件。项目合作开发者可以独立的复制 .env.example 并重命名为 .env,同时将其修改为正确的本地环境配置:API Key 或者其他必要的值。在这个使用方法中 .env 文件应该添加到 .gitignore 文件中保证永远不会出现在 git 中。
- 
     在根目录下添加 .env 文件,eg: 
DB_HOST=127.0.0.1
DB_NAME=timeseriesmonitor
DB_PORT=5432
DB_USER=tsm
DB_UNSECURE=true
- 
     引入 dotenv:npm install dotenv 
const dotenv = require('dotenv');
dotenv.config('./env');  // .env 中的环境变量被加载到 `process.env` 中
console.log(`process.env`);
- 
     打印log如下: 
{
  ...
  DB_HOST: '127.0.0.1',
  DB_NAME: 'timeseriesmonitor',
  DB_PORT: '5432',
  DB_UNSECURE: 'true',
  DB_USER: 'tsm',
  ...
}
.env 源码解读
核心代码逻辑在 lib/main.js 中,可以看到刚开始先初始化了几个正则表达式
正则表达式
const RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*(.*)?\s*$/
const RE_NEWLINES = /\\n/g
const NEWLINES_MATCH = /\r\n|\n|\r/
我们可以通过 regexr.com 来交互式查看表达式各部分的含义:
- 
     const RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*(.*)?\s*$/匹配 .env 文件中的环境变量,如 DB_HOST=127.0.0.1,注意到表达式中有两个部分被 () 包起来了,这是为了后续正则匹配的时候方便提取出匹配的字符串,即环境变量 key,value 的值
- 
     const RE_NEWLINES = /\\n/g匹配 \n 字符串,\:前面的 \ 将后面的 \ 转义掉了,所以这里匹配的是 \n 字符串,而不是换行符。
- 
     const NEWLINES_MATCH = /\r\n|\n|\r/匹配换行符:三种换行符是为了兼容各操作系统,不同操作系统换行符有所不同:\n: Unix系统,\r\n: Windows系统,\r: Mac系统
回车 \r 本义是光标重新回到本行开头,r 的英文 return,控制字符为 CR,即 Carriage Return; 
换行 \n 本义是光标往下一行(不一定到下一行行首),n 的英文 newline,控制字符为 LF,即 Line Feed; 
parse 函数
💡 将 .env 中的字符串转换成 Object
核心逻辑简化如下:
function parse(src) {
  const obj = {}
  
  // 用 NEWLINES_MATCH 分割每行表达式,再 forEach 依次处理
  src.split(NEWLINES_MATCH).forEach(function (line, idx) {
    // 用 RE_INI_KEY_VAL 匹配 'KEY=VAL' 中的 'KEY' 和 'VAL'
    const keyValueArr = line.match(RE_INI_KEY_VAL)
    // matched?
    if (keyValueArr != null) {
      const key = keyValueArr[1]
      const val = (keyValueArr[2] || '').trim()
      obj[key] = val
    }
  })
  return obj
}
其关键是用了 match 方法匹配 'KEY=VAL' 中的 'KEY' 和 'VAL'。match 方法若匹配到了 line 中的键值对则会返回一个数组,这个数组的第一项是整个正则表达式所匹配的字符串,后面会接表达式中用 () 包围起来的正则表达式匹配的字符串。所以 key = keyValueArr[1],val = keyValueArr[2]。
config 函数
💡 将 .env 中的环境变量加载到 process.env 中
核心逻辑简化如下:
function config(options) {
  let dotenvPath = path.resolve(process.cwd(), '.env')
  const parsed = parse(fs.readFileSync(dotenvPath, { encoding: 'utf8' }))
  Object.keys(parsed).forEach(function (key) {
    if (!Object.prototype.hasOwnProperty.call(`process.env`, key)) {
      `process.env`[key] = parsed[key]
    }
  })
  return { parsed }
}
config 函数分三步:
- 
     获取 .env 文件的路径并读取文件 
- 
     将文件的字符串传入 parse 函数中解析成 Object 
- 
     遍历 Object 将其加载到 process.env中
注意代码中使用 Object.prototype.hasOwnProperty.call(process.env, key) 判断 key 是否已经存在于 process.env 中,若存在,则不进行覆盖。使用 Object.prototype.hasOwnProperty 是避免原型链查找,只判断 key 是否存在于 process.env 中,而不是其原型链上,这样做可以省去原型链查找的耗时。
总结
dotenv 源码非常简短只有 118 行,但其有 14.5k star(截止至本文写稿时间),源码简单易懂,建议自己动手看看。
参考资料
github 仓库地址:https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fmotdotla%2Fdotenv
Nodejs 环境下 .env 配置环境变量:http://www.4k8k.xyz/article/weixin_40817115/86189969
作者:前端唯一深情 链接:https://juejin.cn/post/7040397337138561054
我组建了技术交流群,里面有很多 大佬,欢迎进来交流、学习、共建。回复加群即可。
“分享、点赞、在看” 支持一波👍
