问题背景
在使用 Node.js 的 ESM (ECMAScript Modules) 模式(即 package.json 中设置 "type": "module")时,我们不能再像 CommonJS 那样同步加载 .env 文件:
js
// ❌ 不再适用:ESM 中 require 是未定义的
require('dotenv').config();
如果想在 ESM 中加载环境变量,常见的做法是:
js
// ✅ 异步方式
import dotenv from 'dotenv';
dotenv.config();
但这里有个陷阱 :config() 虽然执行了,但它是同步 API (尽管底层读文件是异步的),然而,如果你依赖 .env 的值来初始化其他模块(如数据库连接、第三方 SDK 等),而这些模块在顶层 import 时就需要环境变量,就可能出现环境变量尚未加载完成,模块已开始初始化的问题。
更复杂的情况是,如果你使用的是异步配置加载(比如从远程拉取配置),那就必须确保主应用逻辑在配置加载完成后才执行。
解决方案:使用 -r dotenv/config
Node.js 提供了 -r(--require)参数,可以在运行主模块之前,先加载并执行指定的模块。
dotenv 库恰好提供了一个可以直接被 -r 使用的入口:dotenv/config。
原理
当你运行:
bash
node -r dotenv/config ./bin/www.js
Node.js 会:
- 先加载并执行
dotenv/config模块; dotenv/config会自动调用dotenv.config(),同步加载.env文件;- 最后才加载并执行你的主文件
./bin/www.js。
这样,在你的应用代码运行之前,环境变量就已经准备好了,完美避免了 ESM 中因模块加载顺序导致的变量未定义问题。
实际应用示例
1. package.json 脚本配置
json
{
"name": "nodejs-express",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "cross-env NODE_ENV=development PORT=9001 nodemon",
"start": "node -r dotenv/config ./bin/www.js"
},
"dependencies": {
"dotenv": "^17.2.3",
"express": "^4.21.2"
// ... 其他依赖
},
"devDependencies": {
"nodemon": "^3.1.0",
"cross-env": "^10.1.0"
}
}
✅ 注意:
-r dotenv/config只在start脚本中使用,因为生产/直接运行需要确保环境变量优先加载。
2. nodemon 调试配置(nodemon.json)
开发时我们常用 nodemon,它也需要知道在重启时先加载 dotenv:
json
{
"watch": ["."],
"ext": "js,json",
"ignore": ["dist/", "node_modules/"],
"exec": "node -r dotenv/config ./bin/www.js"
}
💡
nodemon的exec字段指定了每次重启时运行的命令,这里同样加入-r dotenv/config,确保热重载时环境变量依然能正确加载。
3. 开发环境脚本增强(使用 cross-env)
你可能会发现 dev 脚本中用了 cross-env 设置 NODE_ENV 和 PORT,但没加 -r dotenv/config。这是因为 nodemon 本身会读取 nodemon.json 的 exec 配置,所以不需要在 package.json 中重复写。
如果你想统一行为,也可以直接写成:
json
"dev": "nodemon -r dotenv/config ./bin/www.js"
并移除 nodemon.json 中的 exec,效果一样。
高级技巧:传递参数给 dotenv/config
你还可以通过环境变量向 dotenv/config 传参,例如:
bash
node -r dotenv/config ./bin/www.js dotenv_config_path=./.env.development
常用参数:
dotenv_config_path:指定.env文件路径dotenv_config_debug=true:开启调试输出dotenv_config_encoding=utf8:指定编码
例如:
json
"start:prod": "node -r dotenv/config ./bin/www.js dotenv_config_path=./.env.production"
总结
| 场景 | 推荐做法 |
|---|---|
ESM 项目加载 .env |
使用 node -r dotenv/config |
配合 nodemon 开发 |
在 nodemon.json 的 exec 中加入 -r dotenv/config |
| 指定不同环境文件 | 通过 dotenv_config_path 传参 |
| 确保模块初始化顺序 | 利用 -r 实现"启动前注入",避免异步加载竞争 |
✅ 核心价值 :
-r dotenv/config是 ESM 项目中最简洁、最可靠 的环境变量加载方式,无需修改主代码,通过启动命令即可确保.env优先加载,特别适合 Express、Fastify 等框架项目。
📌 一句话口诀:
ESM 加载
.env别慌,-r dotenv/config来帮忙。