- 开发工具
- 后端
【免费下载链接】dotenv
Loads environment variables from .env for nodejs projects.
dotenv 是一个零依赖的 Node.js 模块,用于将.env文件中的环境变量加载进process.env为核心骨架,结合 lib/main.js、cli.js、lib/config-options.js 等源码与 tests/ 下的测试用例,系统讲解安装接入、CLI 用法、全部配置项、解析引擎规则,以及底层实现原理。读完本文,你将能独立完成.env文件的创建、加载、多文件合并、调试与 CLI 注入,并能按需启用fast快解析器或编写基于parse/populate的扩展插件。
快速开始
安装
在项目根目录执行:
npm install dotenv --savedotenv无任何运行时依赖,包体积与解析成本都保持极简(见 package.json 的dependencies为空)。本包自带 CLI 命令,bin字段指向dist/index.cjs,安装后npx dotenv或dotenv均可直接调用。
创建 .env 文件
在项目根目录新建.env:
# .env HELLO="Dotenv" OPENAI_API_KEY="your-api-key-goes-here"加载并读取
在应用代码中尽早导入并配置:
// index.js require('dotenv').config() // 或使用 ESM 方式:import 'dotenv/config' console.log(`Hello ${process.env.HELLO}`)运行:
$ node index.js ◇ injected env (2) from .env Hello Dotenv◇ injected env (2) from .env是加载成功后的注入提示,表示从.env中注入了 2 个变量。提示信息输出到stderr而非 stdout,因此不会污染管道化的标准输出(见 CHANGELOG.md v18.0.0 变更记录)。
从源码看,加载流程在 lib/main.js 的configDotenv中实现:先按默认路径path.resolve(process.cwd(), '.env')解析,fs.readFileSync读取文件,再交给DotenvModule.parse解析,最后DotenvModule.populate写入process.env,并把实际写入的键值集合(populated)数量打印出来。
CLI 用法
dotenv 从 v18.0.0 起自带 CLI(见 CHANGELOG.md 的 v18.0.0 条目),非常适合在启动命令前注入环境变量,也便于 CI、Docker 与 coding agent 使用。
// index.js console.log(`Hello ${process.env.HELLO}`)$ npx dotenv run -- node index.js ◇ injected env (2) from .env Hello Dotenv--分隔符是可选的;dotenv 自己的选项必须放在命令之前,命令之后的所有参数原样透传给目标命令:
$ dotenv run node index.js $ dotenv run -q node index.js $ dotenv run --override --debug -- node index.js $ dotenv run -f .env.local,.env node index.jsCLI 的完整入口在 cli.js 的run()函数:parseRunArgs负责解析参数,loadEnvFiles读取并合并各文件,随后通过 lib/spawn-command.js 的spawnCommand派生子进程执行命令,并把子进程的退出码原样转发(见 tests/test-cli.js 中“preserves child exit code”用例)。
多文件加载与优先级
-f/--file支持一次选择多个.env文件,用逗号分隔或重复该标志均可,文件按给定顺序加载:
$ dotenv run --file .env.local,.env node index.js ◇ injected env (2) from .env.local, .env- 未指定
--override时:先加载的值优先(第一个值胜出),已存在于process.env中的变量不会被覆盖; - 指定
--override时:后加载的值覆盖先前的值(最后一个值胜出),并覆盖已存在的环境变量。
CLI 在 cli.js 的loadEnvFiles中先解析各文件到临时对象parsedAll,最后一次性 populate 进process.env;合并顺序与override语义和 SDK 的config({ path: [...] })完全一致。
退出码与文件缺失行为
CLI 会转发子命令的退出状态。默认.env缺失时允许继续运行(ENOENT被吞掉);但用-f显式指定的文件缺失时会停止执行并报错(对应 cli.js 中options.defaultPath的判断逻辑)。
进阶用法
ES6 导入
import 'dotenv/config'DOTENV_ENCODING、DOTENV_PATH、DOTENV_QUIET、DOTENV_DEBUG、DOTENV_OVERRIDE、DOTENV_FAST为config()和dotenv run提供默认值。优先级从高到低为:直接传入的选项/标志 >DOTENV_*环境变量 > 旧的DOTENV_CONFIG_*名称。注意:空值和 false 值不会触发回退到旧名称。
其他包管理器
bun add dotenv yarn add dotenv pnpm add dotenv deno add dotenvMonorepo 场景
对于apps/backend/app.js这类结构,把.env放在app.js进程实际运行的目录下即可:
# app/backend/.env S3_BUCKET="YOURS3BUCKET" SECRET_KEY="YOURSECRETKEYGOESHERE"因为默认路径是基于process.cwd()(当前工作目录)解析的。
多行值
从 v15.0.0 起支持多行变量(例如私钥),可以直接换行书写:
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY----- ... Kh9NV... ... -----END RSA PRIVATE KEY-----"也可以双引号包裹并使用\n转义:
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nKh9NV...\n-----END RSA PRIVATE KEY-----\n"注意:只有双引号包裹的值才会展开\n换行。这在 tests/test-parse.js 中有明确验证:EXPAND_NEWLINES="expand\nnew\nlines"会展开为真实换行,而单引号或未引用的DONT_EXPAND_UNQUOTED、DONT_EXPAND_SQUOTED会保留字面\n。
注释
.env支持整行注释和行内注释:
# This is a comment SECRET_KEY=YOURSECRETKEYGOESHERE # comment SECRET_HASH="something-with-a-#-hash"从 v15.0.0 起(破坏性变更):只要出现#即视为注释开始,因此值里若含#必须用引号包裹。
解析引擎(Parse)
解析引擎可独立使用,接受 String 或 Buffer,返回键值对象:
const dotenv = require('dotenv') const buf = Buffer.from('BASIC=basic') const config = dotenv.parse(buf) // 返回对象 console.log(typeof config, config) // object { BASIC : 'basic' }使用 dotenvx 实现变量展开、命令替换、加密与多环境
以下高级能力由 dotenvx 提供(dotenv本身刻意保持单文件解析、不做展开/加密):
变量展开:引用并展开本机已有变量,写入
.env:# .env USERNAME="username" DATABASE_URL="postgres://${USERNAME}@localhost/my_database"$ dotenvx run --debug -- node index.js ⟐ injected env (2) from .env · dotenvx@1.59.1 DATABASE_URL postgres://username@localhost/my_database命令替换:把命令输出塞进变量:
# .env DATABASE_URL="postgres://$(whoami)@localhost/my_database"加密:一条命令给
.env文件加解密:$ dotenvx set HELLO Production -f .env.production $ echo "console.log('Hello ' + process.env.HELLO)" > index.js $ DOTENV_PRIVATE_KEY_PRODUCTION="<.env.production private key>" dotenvx run -- node index.js ⟐ injected env (2) from .env.production · dotenvx@1.59.1 Hello Production多环境:按环境建文件,用
-f加载:$ echo "HELLO=production" > .env.production $ dotenvx run -f=.env.production -- node index.js Hello production多个
.env文件时,先指定的优先:$ echo "HELLO=local" > .env.local $ echo "HELLO=World" > .env $ dotenvx run -f=.env.local -f=.env -- node index.js Hello local生产部署:创建
.env.production→dotenvx encrypt -f .env.production加密 → 在服务器上设置解密密钥DOTENV_PRIVATE_KEY_PRODUCTION→ 把加密后的.env.production提交进代码库并部署 → 运行时由dotenvx run -- node index.js自动解密注入。同步(Syncing):用
dotenvx encrypt -f .env加密后随 git 安全同步,解密密钥与代码分离,依然符合 Twelve-Factor 原则。
FAQ 速查
| 问题 | 结论 |
|---|---|
应该提交.env文件吗? | 不建议。除非用 dotenvx 加密,加密后反而推荐提交。 |
| 变量展开怎么做? | 使用 dotenvx。 |
要不要多个.env文件? | 每个环境一个文件(.env本地、.env.production生产),避免自定义继承式配置,必要时跨环境复制值。 |
用import怎么写? | import 'dotenv/config'置于读取环境变量的模块之前;或dotenv run -- node index.mjs。 |
| 可以写插件吗? | 可以。dotenv.config()返回包含parsed键的对象,可直接传给dotenv-expand等插件继续加工。 |
| 已存在的环境变量怎么办? | 默认绝不覆盖(冲突时跳过.env中的同名项);需要覆盖时用override选项。 |
| 变量在 React 里不生效? | React 跑在 Webpack 中,process.env只能通过 Webpack 配置注入;react-scripts内置 dotenv 但要求变量以REACT_APP_前缀。 |
.env加载失败? | 多半是文件位置不对。开启debug: true查看控制台错误。 |
报Module not found: Can't resolve 'os|path'? | 前端场景缺少 polyfill,安装node-polyfill-webpack-plugin并在webpack.config.js中配置,或直接使用 dotenv-webpack。 |
解析引擎规则清单(来自 README FAQ,测试逐一验证)
BASIC=basic→{BASIC: 'basic'}- 空行跳过
- 以
#开头的行视为注释 #标记注释开始(值被引号包裹时除外)- 空值变成空字符串(
EMPTY=→{EMPTY: ''}) - 保留内部引号(类 JSON):
JSON={"foo": "bar"}→{JSON:"{\"foo\": \"bar\"}"} - 未引用值的两端空白被去除:
FOO= some value→{FOO: 'some value'} - 单双引号包裹的值会被脱引号:
SINGLE_QUOTE='quoted'→{SINGLE_QUOTE: "quoted"} - 单双引号包裹的值保留两端空白:
FOO=" some value "→{FOO: ' some value '} - 双引号值展开换行:
MULTILINE="new\nline"→{MULTILINE: 'new\nline'} - 支持反引号:
BACKTICK_KEY=`This has 'single' and "double" quotes inside of it.`
这些规则与 tests/.env 测试样本及 tests/test-parse.js 中的断言一一对应,例如INLINE_COMMENTS、EQUAL_SIGNS=equals==、SPACED_KEY、export EXPORT_IS_DECLARED=parsed(export关键字会被忽略)等。
SDK 参考:config / parse / populate
dotenv 暴露三个函数:config、parse、populate(类型声明见 lib/main.d.ts)。
config()
读取.env→ 解析 → 写入process.env,返回包含parsed或error键的对象:
const result = dotenv.config() if (result.error) { throw result.error } console.log(result.parsed)选项:path
- 默认:
path.resolve(process.cwd(), '.env') - 指定自定义路径(也支持
URL对象):
require('dotenv').config({ path: '/custom/path/to/.env' }) const fileUrl = new URL('file:///custom/path/to/.env') require('dotenv').config({ path: fileUrl })- 传数组可加载多个文件,按顺序解析并与
process.env(或option.processEnv)合并;未设override时首个值胜出,设了则末个值胜出:
require('dotenv').config({ path: ['.env.local', '.env'] })选项:quiet
- 默认:
false - 抑制运行时日志。
dotenv/config导入与 preload 方式默认即为true;可用DOTENV_QUIET=false(或旧名DOTENV_CONFIG_QUIET=false)在 shell 里重新开启启动信息。
require('dotenv').config({ quiet: false }) // 改为 true 可抑制输出选项:encoding
- 默认:
utf8 - 指定
.env文件编码:
require('dotenv').config({ encoding: 'latin1' })选项:debug
- 默认:
false - 开启日志,定位键值未被按预期写入的原因:
require('dotenv').config({ debug: process.env.DEBUG })选项:override
- 默认:
false - 用
.env的值覆盖本机已设置的环境变量;多文件时配合path数组逐文件生效。未设置时首个值胜出,设置后末个值胜出:
require('dotenv').config({ override: true })选项:fast
- 默认:
false - 启用约 2 倍速的字符扫描解析器(character-scanner parser),默认仍是经典正则解析器:
require('dotenv').config({ fast: true })选项:processEnv
- 默认:
process.env - 指定一个自定义对象作为写入目标,默认写
process.env:
const myObject = {} require('dotenv').config({ processEnv: myObject }) console.log(myObject) // 来自 .env 的值 console.log(process.env) // 未被改动parse()
解析引擎独立可用,接受 String 或 Buffer,返回键值对象(可选{ fast: true }):
const dotenv = require('dotenv') const buf = Buffer.from('BASIC=basic') const config = dotenv.parse(buf) console.log(typeof config, config) // object { BASIC : 'basic' }parse支持debug选项,输入不合规时会输出调试信息:
const dotenv = require('dotenv') const buf = Buffer.from('hello world') const opt = { debug: true } const config = dotenv.parse(buf, opt) // 因输入不是 KEY=VAL 形式,会看到一条 debug 消息populate()
把解析结果写入目标的引擎,接受 target、source 与 options,适合自定义对象的高级用户:
const dotenv = require('dotenv') const parsed = { HELLO: 'world' } dotenv.populate(process.env, parsed) console.log(process.env.HELLO) // world自定义 source 与 target,并开启override与debug:
const dotenv = require('dotenv') const parsed = { HELLO: 'universe' } const target = { HELLO: 'world' } dotenv.populate(target, parsed, { override: true, debug: true }) console.log(target) // { HELLO: 'universe' }populate支持两个选项:debug(默认false,输出排查日志)与override(默认false,是否覆盖已存在的变量)。
从 lib/main.js 的实现看,populate逐键遍历parsed:若目标对象已存在该键且override为真则覆盖,否则跳过;不存在的键直接写入,并统计实际写入的populated集合返回。若 target/source 不是对象,会抛出code为OBJECT_REQUIRED的错误(对应 tests/test-populate.js 的断言)。
CLI 参考
dotenv命令随包自带,可用npx dotenv或直接在 npm scripts 中使用。
run
从.env加载环境变量,然后运行命令:
npx dotenv run [options] -- <command> [args...]npx dotenv run -- node index.js npx dotenv run -f .env.local -- node index.js npx dotenv run -f .env.local -f .env -- npm testdotenv 选项放在命令之前;--分隔符可选,命令后的所有参数透传给目标命令(tests/test-cli.js 验证了空格、引号、空参数与 shell 元字符的透传保真)。
CLI 选项表
| 选项 | 说明 |
|---|---|
-f, --file <paths> | 加载一个或多个文件。可重复标志或用逗号分隔。默认.env。 |
-q, --quiet | 抑制 “injected environment variables” 提示消息。 |
--debug | 开启 debug 日志。 |
--override | 覆盖已有环境变量。加载多文件时最后一个值胜出。 |
--fast | 使用更快的字符扫描解析器。 |
-h, --help | 显示帮助。也可用dotenv --help。 |
未设--override时,已有环境变量优先,多文件间先加载的值胜出;设了--override则文件值覆盖已有变量,且后加载的文件覆盖先加载的。
环境变量默认值表
可用环境变量设置 CLI 默认值(CLI 标志优先于它们):
| 变量 | 默认 | 说明 |
|---|---|---|
DOTENV_PATH | .env | 要加载的文件路径。 |
DOTENV_ENCODING | utf8 | 文件编码。 |
DOTENV_QUIET | false | 抑制注入提示消息。 |
DOTENV_DEBUG | false | 开启 debug 日志。 |
DOTENV_OVERRIDE | false | 覆盖已有环境变量。 |
DOTENV_FAST | false | 使用更快的解析器。 |
旧的DOTENV_CONFIG_*名称在对应DOTENV_*未设置时作为回退。布尔类设置中,false、0、no、off与空值均视为关闭。
源码级原理
双解析器:正则解析与 fast 字符扫描
lib/main.js 内置两条解析路径(tests/test-parse-fast.js 专门验证 fast 模式):
- 默认
parseRegex(lib/main.js):基于LINE正则逐行匹配KEY=VAL,处理export前缀、引号脱壳、双引号\n/\r展开。 parseFast(lib/main.js):手写字符扫描器(源自 PR #1010),用KEY_CHAR查找表(A-Za-z0-9._-)扫描键名,逐字符判断注释、export前缀、引号配对,无正则回溯开销,官方标注约 2 倍速,通过{ fast: true }、CLI--fast或DOTENV_FAST=true开启。两套解析器对同一输入产生一致结果,只是性能路径不同。
配置合并链路:config-options.js
lib/config-options.js 是配置层的枢纽:
parseBoolean:把字符串'false' | '0' | 'no' | 'off' | ''归一为false,其余字符串为true,非字符串走Boolean()。optionsFromEnv:按ENCODING / PATH / QUIET / DEBUG / OVERRIDE / FAST顺序读取DOTENV_*,缺失时回退DOTENV_CONFIG_*;其中ENCODING与PATH保留字符串,其余经parseBoolean转布尔。
configDotenv通过{ ...optionsFromEnv(), ...options }合并:环境变量提供默认值,显式传入的 options 覆盖前者(tests/test-config-options.js 验证了别名优先级与显式 false/空值不回退的行为)。
CLI 信号与进程管理
cli.js 在派生子进程后做了完整的信号管理:
- 交互式终端(
stdin.isTTY)下首次 Ctrl-C 交给前台子进程处理,第二次转发SIGTERM、第三次SIGKILL; - 非交互(CI/服务)场景用独立进程组,
SIGINT/SIGTERM/SIGHUP/SIGQUIT均转发到整个进程组,避免遗留孙进程; - 子进程退出码原样透传(
process.exit(exitCode)),被信号终止时向自身重发信号保持退出语义。
Windows 下由 lib/spawn-command.js 负责cmd.exe参数转义(quoteWindowsArgument、protectShellToken)、PATHEXT可执行文件解析与.bat/.cmd/npm shim 的双重转义,相关行为在 tests/test-cli.js 的 Windows 用例中覆盖。
类型声明
lib/main.d.ts 为 TypeScript 用户提供了完整类型:DotenvConfigOptions(path: string | string[] | URL、encoding、quiet、debug、override、fast、processEnv)、DotenvConfigOutput({ error?, parsed? })以及parse/populate的签名,类型测试见 tests/types/test.ts。
相关工具
- dotenv-expand:展开
.env中的变量引用。 - dotenv-vscode:在 VS Code 中隐藏/管理密钥。
- dotenvx:为
.env提供加密、多环境与同步能力(.env.vault支持在 v18.0.0 已移除,见 CHANGELOG.md)。
完整变更历史见 CHANGELOG.md:v18.0.0 引入 CLI 与 fast 解析器,v18.0.3 修复.env内设置DOTENV_QUIET的问题,v18.0.4 让import dotenv/config默认静默(quiet: true)。
小结
从.env文件的创建、config()加载、CLIdotenv run注入,到parse/populate独立引擎与双解析器原理,dotenv 以极小的 API 面覆盖了环境变量管理的完整闭环。实际使用建议:本地/开发用.env,生产单独维护.env.production并配合 dotenvx 加密;遇到加载异常优先开启debug: true定位;追求启动性能时启用fast解析器。仓库内 tests/ 目录保留了全部行为契约,可作为深入研读的起点。
- 开发工具
- 后端
【免费下载链接】dotenv
Loads environment variables from .env for nodejs projects.
相关推荐
python-dotenv 实战指南:.env 文件解析、环境变量加载与 CLI 操作全解析
python dotenv 实战指南:.env 文件解析、环境变量加载与 CLI 操作全解析 python dotenv 是一个从 .env 文件读取键值对并将
后端scan4all 依赖解析:gotenv 库加载 .env 环境变量的完整实践指南
scan4all 依赖解析:gotenv 库加载 .env 环境变量的完整实践指南 导读 本指南围绕 Go 开源库 gotenv( 仓库内完整源码 https:
网络安全漏洞扫描渗透测试应用安全Node.js环境变量终极配置指南:dotenv模块安全使用详解
Node.js环境变量终极配置指南:dotenv模块安全使用详解 在Node.js应用开发中,环境变量配置是确保应用安全性和灵活性的关键环节。node expr
后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考