Node.js:本地跑脚本时可用 --env-file 读 .env

55 次浏览6 条回复

写本地 Node 小脚本时,环境变量不多的话不一定要先装加载库。Node 20.6+ 可以直接带 --env-file。

建两个文件:

printf 'PORT=3100\nMODE=dev\n' > .env
printf 'console.log(`port=${process.env.PORT}, mode=${process.env.MODE}`)\n' > app.mjs

然后运行:

node --env-file=.env app.mjs

会输出:

port=3100, mode=dev

.env 里要放密钥的话,还是记得加进 .gitignore。线上环境通常由部署平台或进程管理器注入变量,这个更适合本地开发和一次性脚本。

这个对一次性脚本挺省事。补一个容易忽略的点:如果启动进程时已经有同名环境变量,process.env 里的值会优先于 .env,所以排查配置时最好先看一下 env | grep '^PORT=' 之类的输出。还有 .env 建议只提交 .env.example,避免密钥误进仓库。

再补一个配置解析上的区别:Node 的 --env-file 不是 shell,.env 里写 ROOT=$HOME 或 URL=http://localhost:${PORT} 时,不要默认它会继续展开变量。需要组合值的话,放到程序启动后按自己的校验和拼接逻辑处理;.env 更适合保存字面量配置。

还有个实际坑:脚本如果再启动子进程,默认会继承当前的 process.env,但用了 spawn/execFile 的 env 选项后就可能被覆盖。需要传配置时,建议显式写 env: { ...process.env, MODE: 'test' },这样不会把 PATH 之类的基础变量一并丢掉。

别熬夜啦Lv1#3

还有个实际坑:脚本如果再启动子进程,默认会继承当前的 process.env,但用了 spawn/execFile 的 env 选项后就可能被覆盖。需要传配置时,建议显式写 env: { ...process.env, MODE: 'test' },这样不会把 PATH 之类的基础变量一并丢掉。

再补个类型坑:process.env.PORT 读出来始终是字符串。要拿来做端口号或数值比较,最好显式 const port = Number(process.env.PORT),再检查 Number.isInteger(port) 和范围;不然空值或拼写错误可能一路变成 NaN,到监听时才发现。

MukiLv1#4

再补个类型坑:process.env.PORT 读出来始终是字符串。要拿来做端口号或数值比较,最好显式 const port = Number(process.env.PORT),再检查 Number.isInteger(port) 和范围;不然空值或拼写错误可能一路变成 NaN,到监听时才发现。

对,Number('') 还会得到 0,只判断 Number.isInteger 也可能把空配置放过去。我会先判断原始值是不是非空字符串,再转数字并限制在 1..65535,配置错误尽早 throw,比等到 listen() 报错好定位。

这个线程把常见坑补得差不多了。再提一个适合分层配置的用法:--env-file 可以传多次,后面的文件覆盖前面的同名变量。比如把不敏感的默认值放 .env,本机覆盖放 .env.local:

node --env-file=.env --env-file=.env.local app.mjs

不过密钥还是尽量交给运行环境注入,别把本机配置文件顺手提交进去。