dotenv:一个用于读取 Node 环境变量的工具库

本文是“源码解读”系列第 1 篇。

概述

dotenv:一个用于读取 Node 环境变量的工具库

dotenv 是一个 npm 包,用于加载 .env 文件中定义的环境变量赋值给到 process.env 中。process.env[1] 是 Node.js 程序在运行时能访问到的一个全部变量,存储环境变量信息。

下面我们介绍一下 dotenv 的基本安装、使用。

安装。

# install locally (recommended)
npm install dotenv --save

然后,在项目的根目录下创建一个 .env 文件。

S3_BUCKET="YOURS3BUCKET"
SECRET_KEY="YOURSECRETKEYGOESHERE"

接着,就可以在项目入口文件里使用 dotenv 加载这个 .env 文件了。process.env 上会看到添加的这两个自定义环境变量。

require('dotenv').config()
console.log(process.env) // remove this after you've confirmed it is working
/*
{
  ...
 S3_BUCKET: 'YOURS3BUCKET',
  SECRET_KEY: 'YOURSECRETKEYGOESHERE'
  ...
} */

接下来,我们看下 dotenv 的源码实现。

源码解读

我们以 dotenv v16.3.1[2] 版本为例。

找到源码

先找到 dotenv 的仓库地址(motdotla/dotenv[3]),require('dotenv').config() 是从 package.json"main" 字段加载文件的。

{
  "main""lib/main.js",
}

所以,源码位于 lib/main.js。查看代码,找到导出 .config() 方法的地方[4]

const DotenvModule = {
  config,
}
module.exports.config = DotenvModule.config
module.exports = DotenvModule

注:源码很多,我们只关注我们使用的部分实现。下文同理。

分析 .config() 方法

.config() 方法定义在 DotenvModule.config 中。

// Populates process.env from .env file
function config (options{
  // fallback to original dotenv if DOTENV_KEY is not set
  if (_dotenvKey(options).length === 0) {
    return DotenvModule.configDotenv(options)
  }
}

当我们 require('dotenv').config() 方式使用 dotenv 时,实际起作用的是上面几行代码。

_dotenvKey() 这个以下划线(_)作为前缀的方法表示 dotenv 内部方法。_dotenvKey()  方法检查是否有 options.DOTENV_KEY 或 process.env.DOTENV_KEY 存在[5],没有则返回空字符串('')。

因此,实际是调用 DotenvModule.configDotenv(options) 加载的 .env 文件。来看下它是如何实现的。

.configDotenv(options) 的源码[6]如下:

function configDotenv (options{
  let dotenvPath = path.resolve(process.cwd(), '.env')
  let encoding = 'utf8'

  try {
    // Specifying an encoding returns a string instead of a buffer
    const parsed = DotenvModule.parse(fs.readFileSync(dotenvPath, { encoding }))

    let processEnv = process.env

    DotenvModule.populate(processEnv, parsed, options)

    return { parsed }
  } catch (e) {
    return { error: e }
  }
}

实现步骤是这样的:

  1. 加载当前工作目录(通常是项目根目录)下的 .env 文件
  2. .env 文件格式化(DotenvModule.parse()
  3. 将格式化之后的 .env 文件内容(自定义环境变量)赋值给 process.envDotenvModule.populate

看一下 DotenvModule.parse() 的源码实现:

// Parse src into an Object
function parse (src{
  const obj = {}
  const LINE = /(?:^|^)s*(?:exports+)?([w.-]+)(?:s*=s*?|:s+?)(s*'(?:\'|[^'])*'|s*"(?:\"|[^"])*"|s*`(?:\`|[^`])*`|[^#rn]+)?s*(?:#.*)?(?:$|$)/mg
  // Convert buffer to string
  let lines = src.toString()

  // Convert line breaks to same format
  lines = lines.replace(/rn?/mg'n')

  let match
  while ((match = LINE.exec(lines)) != null) {
    const key = match[1]

    // Default undefined or null to empty string
    let value = (match[2] || '')

    // Remove whitespace
    value = value.trim()
  }

  return obj
}

实现步骤是这样的:

  1. 先将 .env 文件里的内容一行行匹配(match = LINE.exec(lines)
  2. 找出每一行等于号(=)左右的 key 和 value,赋值给 obj,并返回

这块比较难理解是 LINE 这个正则表达式,比较长,这是一个符合规范的严谨写法,生产环境也应该这么用。不过为了方便学习,我们可以把它简化成下面这样:

const LINE = /s*([w.-]+)(?:s*=s*?)(s*"[^"]*"|[^#]+)s*/mg

3 个圆括号分别对应 key(([w.-]+))、等于 =((?:s*=s*?)) 和 value((s*"[^"]*"|[^#]+))。

匹配规则的一个演示 demo 如下(线上地址[7]):

dotenv:一个用于读取 Node 环境变量的工具库

关于 value 的匹配规则:"" 里的值;否则全部匹配(同时排除 # 及之后的内容,# 作为注释标记)

dotenv:一个用于读取 Node 环境变量的工具库

内置的 --env-file  参数支持

从 20.6.0 版本开始,Node.js 现在内置了对 `.env` 文件的支持[8]。可以通过 --env-file 参数指定的文件就能向 process.env 上添加自定义环境变量了。

$ node --env-file=.env index.js
# 同时还支持指定多环境文件,后者会覆盖前者里的同名属性
$ node --env-file=.env --env-file=.development.env index.js 

假设我们定义了以下的 .env 文件内容:

# .env
PASSWORD=supersecret
API_KEY=84de8263ccad4d3dabba0754e3c68b7a

测试结果如下:

$ node --env-file .env
Welcome to Node.js v20.6.0.
Type ".help" for more information.
> console.log(process.env.PASSWORD)
supersecret
undefined
> console.log(process.env.API_KEY)
84de8263ccad4d3dabba0754e3c68b7a
undefined

不过这个特性目前还处于实验阶段,还不能放心的用于生产,目前的最佳方案还是使用 dotenv。

接下来要做什么

至此,我们介绍了 dotenv 基本用法以及实现原理。希望对大家日后使用有更深的理解。

有兴趣的同学可以继续在源码仓库中探索。可以从以下几个方面着手:

  • options 选项对象的支持和判断
  • 对异常错误的处理
  • 对导出 ES 模块的支持

好了,就到这里。大家编码愉快!

参考资料

[1]

process.env: https://stackabuse.com/bytes/reading-environment-variables-in-node-js/

[2]

dotenv v16.3.1: https://github.com/motdotla/dotenv/blob/v16.3.1

[3]

motdotla/dotenv: https://github.com/motdotla/dotenv/

[4]

导出 .config() 方法的地方: https://github.com/motdotla/dotenv/blob/v16.3.1/lib/main.js#L309

[5]

_dotenvKey()  方法检查是否有 options.DOTENV_KEY 或 process.env.DOTENV_KEY 存在: https://github.com/motdotla/dotenv/blob/v16.3.1/lib/main.js#L102

[6]

.configDotenv(options) 的源码: https://github.com/motdotla/dotenv/blob/v16.3.1/lib/main.js#L182

[7]

线上地址: https://regex101.com/r/ED9Stg/1

[8]

Node.js 现在内置了对 .env 文件的支持: https://philna.sh/blog/2023/09/05/nodejs-supports-dotenv/


原文始发于微信公众号(写代码的宝哥):dotenv:一个用于读取 Node 环境变量的工具库

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/243769.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!