全局配置与多环境(开发/测试/生产)切换的通行做法

2026-09-21 82 浏览 0 评论 0 字

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

问题背景

本地联调连的是本机数据库,测试环境连的是测试库,上线又得连生产库——如果每次切换环境都要去改代码里的连接串,既容易漏改,又容易把测试数据写进生产库,那是灾难。配置与环境的分离,就是让 代码不变,只换一套配置 就能在多个环境之间平滑切换。

原理说明

通行的做法是"三重来源,越靠前优先级越高":

  1. 命令行 / 运行时 :进程直接传入,最最优先
  2. 环境变量 : .env 文件或操作系统注入,最常用
  3. 配置文件默认值 :作为兜底

然后用 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 显式转成数字再使用。


发布评论

发布评论前请先 登录。
0 评论
点赞
收藏

评论列表 0

暂无评论