本文是“源码解读”系列第 1 篇。
概述
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 }
}
}
实现步骤是这样的:
-
加载当前工作目录(通常是项目根目录)下的 .env
文件 -
对 .env
文件格式化(DotenvModule.parse()
) -
将格式化之后的 .env
文件内容(自定义环境变量)赋值给process.env
(DotenvModule.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
}
实现步骤是这样的:
-
先将 .env
文件里的内容一行行匹配(match = LINE.exec(lines)
) -
找出每一行等于号( =
)左右的 key 和 value,赋值给obj
,并返回
这块比较难理解是 LINE 这个正则表达式,比较长,这是一个符合规范的严谨写法,生产环境也应该这么用。不过为了方便学习,我们可以把它简化成下面这样:
const LINE = /s*([w.-]+)(?:s*=s*?)(s*"[^"]*"|[^#]+)s*/mg
3 个圆括号分别对应 key(([w.-]+)
)、等于 =((?:s*=s*?)
) 和 value((s*"[^"]*"|[^#]+)
)。
匹配规则的一个演示 demo 如下(线上地址[7]):
关于 value 的匹配规则:""
里的值;否则全部匹配(同时排除 #
及之后的内容,#
作为注释标记)
内置的 --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 模块的支持
好了,就到这里。大家编码愉快!
参考资料
process.env
: https://stackabuse.com/bytes/reading-environment-variables-in-node-js/
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