技术随笔:Node.js ESM 中巧用 `-r dotenv/config` 解决环境变量异步加载问题

问题背景

在使用 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 会:

  1. 先加载并执行 dotenv/config 模块;
  2. dotenv/config 会自动调用 dotenv.config(),同步加载 .env 文件;
  3. 最后才加载并执行你的主文件 ./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"
}

💡 nodemonexec 字段指定了每次重启时运行的命令,这里同样加入 -r dotenv/config,确保热重载时环境变量依然能正确加载。


3. 开发环境脚本增强(使用 cross-env

你可能会发现 dev 脚本中用了 cross-env 设置 NODE_ENVPORT,但没加 -r dotenv/config。这是因为 nodemon 本身会读取 nodemon.jsonexec 配置,所以不需要在 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.jsonexec 中加入 -r dotenv/config
指定不同环境文件 通过 dotenv_config_path 传参
确保模块初始化顺序 利用 -r 实现"启动前注入",避免异步加载竞争

核心价值-r dotenv/config 是 ESM 项目中最简洁、最可靠 的环境变量加载方式,无需修改主代码,通过启动命令即可确保 .env 优先加载,特别适合 Express、Fastify 等框架项目。


📌 一句话口诀

ESM 加载 .env 别慌,-r dotenv/config 来帮忙。

相关推荐
wjs20248 分钟前
DOM CDATA
开发语言
Tingjct10 分钟前
【初阶数据结构-二叉树】
c语言·开发语言·数据结构·算法
猷咪36 分钟前
C++基础
开发语言·c++
IT·小灰灰37 分钟前
30行PHP,利用硅基流动API,网页客服瞬间上线
开发语言·人工智能·aigc·php
快点好好学习吧39 分钟前
phpize 依赖 php-config 获取 PHP 信息的庖丁解牛
android·开发语言·php
秦老师Q40 分钟前
php入门教程(超详细,一篇就够了!!!)
开发语言·mysql·php·db
烟锁池塘柳040 分钟前
解决Google Scholar “We‘re sorry... but your computer or network may be sending automated queries.”的问题
开发语言
是誰萆微了承諾40 分钟前
php 对接deepseek
android·开发语言·php
2601_9498683644 分钟前
Flutter for OpenHarmony 电子合同签署App实战 - 已签合同实现
java·开发语言·flutter
星火开发设计1 小时前
类型别名 typedef:让复杂类型更简洁
开发语言·c++·学习·算法·函数·知识