全局配置与多环境(开发/测试/生产)切换的通行做法
全局配置与多环境隔离是后端工程化的基本功,核心思想是「代码不变,只换一套配置」。本文讲解 NODE_ENV 划分环境、环境变量与配置文件的三重优先来源,以及用 dotenv 按环境加载 .env 文件的通行做法,同时强调密钥必须走环境变量注入、绝不能提交进代码仓库。

问题背景
本地联调连的是本机数据库,测试环境连的是测试库,上线又得连生产库——如果每次切换环境都要去改代码里的连接串,既容易漏改,又容易把测试数据写进生产库,那是灾难。配置与环境的分离,就是让 代码不变,只换一套配置 就能在多个环境之间平滑切换。
原理说明
通行的做法是"三重来源,越靠前优先级越高":
- 命令行 / 运行时 :进程直接传入,最最优先
- 环境变量 :
.env文件或操作系统注入,最常用 - 配置文件默认值 :作为兜底
然后用 NODE_ENV 这个约定俗成的变量来标记当前环境(development / test / production),再决定加载哪套配置。社区里 dotenv 是这个思路的最轻量实现:它把 .env 文件里的变量读进来塞进 process.env ,你的代码统一从 process.env 取值即可。
配置往往要分两类:
- 可暴露的 :端口、日志级别、超时时间
- 绝不可暴露的 :数据库密码、密钥,通过环境变量注入,不许写进配置文件提交到代码仓库
示例代码
用 dotenv 实现多环境加载:
// config/env.js
const dotenv = require('dotenv');
const path = require('path');
const NODE_ENV = process.env.NODE_ENV || 'development';
// 按环境加载对应的 .env 文件,优先级:具体环境 > 通用
dotenv.config({ path: path.resolve(__dirname, `../.env.${NODE_ENV}`) });
dotenv.config(); // 再用通用 .env 补缺
module.exports = { NODE_ENV };统一的配置读取:
// config/index.js
const { NODE_ENV } = require('./env');
const config = {
env: NODE_ENV,
port: parseInt(process.env.PORT || '3000', 10),
db: {
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT || '3306', 10),
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
},
jwtSecret: process.env.JWT_SECRET,
logLevel: process.env.LOG_LEVEL || 'info',
};
module.exports = config;.env.development 与 .env.production :
# .env.development
DB_HOST=127.0.0.1
DB_NAME=myapp_dev
LOG_LEVEL=debug# .env.production
DB_HOST=db-prod.internal
DB_NAME=myapp_prod
LOG_LEVEL=warn生产环境的密钥建议不进仓库,由部署平台注入环境变量, .env.production 不要提交到 Git。
常见坑与解决方案
- 坑一:把数据库密码写进提交到 Git 的配置文件里,直接泄露。解法:密钥只走环境变量注入,
.env.*加入.gitignore,仓库里只留.env.example模板。 - 坑二:
process.env.PORT是字符串,直接+运算会拼接而不是累加。解法:用parseInt显式转成数字。 - 坑三:
.env文件加载顺序反了导致环境变量互相覆盖。解法:先加载具体环境的,再加载通用的,让通用值兜底。 - 坑四:忘了设置
NODE_ENV,在开发环境跑到生产配置。解法:启动脚本里显式指定NODE_ENV=development,部署平台也显式指定。 - 坑五:默认
PORT与 Docker 里映射的端口搞混,容器内监听端口和宿主映射端口是两回事。这也是常踩的。
小结
配置与代码分离,用 NODE_ENV 划分环境,用 dotenv 管理加载顺序,密钥通过环境变量注入而不进仓库,这一套下来环境切换基本无感。把业务逻辑往外拆的下一步,就是 Service 层的活了。
FAQ 常见问题
Q:为什么生产环境千万不要把密钥提交到 Git 仓库?
A:密钥(数据库密码、JWT 密钥等)一旦进仓库,要么泄露在历史提交里,要么被拉取代码的开发者看到。正确做法是部署平台注入环境变量, .env.* 加 .gitignore ,仓库只留 .env.example 模板。
Q: .env 文件的加载顺序反了会出什么问题?
A:会造成环境变量互相覆盖、读到的值不对。正确顺序是先加载具体环境(如 .env.production ),再加载通用 .env 兜底缺省项,这样具体环境的值优先。
Q: process.env.PORT 为什么用起来会得到「字符串拼接」结果?
A:因为环境变量读进来都是字符串。直接做 + 运算会被当作字符串拼接而非数值累加,所以要用 parseInt 显式转成数字再使用。






