为什么配置要交给环境变量而不是写死在代码里
配置是指在不改代码的前提下,为不同环境(本地、测试、生产)改变行为的那部分信息:数据库地址、API 端点、密钥、日志级别等。把它们写死在代码里,等于每次环境变化都要改代码、重新部署,还会引入把测试库地址误带到生产的风险。
12 因素应用的原则核心是"配置存储与代码严格分离"。环境变量是最常见也最不依赖具体框架的载体,无论 Spring、Django 还是 Node 都能通过统一机制读取;同时它天然支持运行时注入,无需重新构建镜像就能切换环境。
代价是要管好它:一旦配置分散在几百个 key 里,命名、缺省值、是否必填都会变成维护负担,所以需要工具约定来兜底,这也是后面几节要做的事。
# 12 因素:同一份代码,靠环境变量区分行为
export DATABASE_URL="mysql://app:***@prod-db:3306/shop"
export LOG_LEVEL="info"
# 代码里不再出现连接串硬编码,只读环境变量环境变量的优先级:别被 .env 里的旧值骗了
同名的环境变量来源很多:系统环境、shell 里 export 的、.env 文件、.env.local、Docker 的 -e、CI 的 secrets。它们之间谁覆盖谁容易让人困惑,而 dotenv 类工具一个常见的坑是"我以为改了 .env,怎么没生效"。
以 Node 的 dotenv 为例,默认行为是"不覆盖已存在的系统变量":也就是如果进程环境里已有同名变量,.env 里的值不会顶替它。这个坑的后果是你在本机 shell 里 export 过一个旧值,之后怎么改 .env 都像没改。
要彻底厘清,记住通用优先级:进程内显式赋值 > shell 环境 > .env.local > .env > 默认值。遇到"改不通"先 echo $KEY 看进程环境到底解析成了什么,再决定是覆盖还是删掉旧的。
# 查看当前生效值(字段解析,别靠猜)
printenv | grep -E '^(DATABASE_URL|NODE_ENV) ='
# Node 默认不覆盖已存在变量
# 强制覆盖需显式声明
require('dotenv').config({ override: true })用 .env.example 立规矩、用 .gitignore 挡住密钥
真正应该提交进 git 的是配置的"模板"而不是值本身。创建 .env.example 或 .env.sample,列出所有 key、写明作用与是否必填,但用占位符代替真实值。新成员 clone 后复制一份 .env 填上自己的本地值即可,这样既统一了命名又不会把密钥带进仓库。
同时务必把真实 .env 加入 .gitignore。一旦历史提交里出现过 .env,单纯加 gitignore 无法抹除——需要在 git history 里改写(filter-branch 或 BFG)并强制推送,并立刻轮换泄露的密钥。这类事故的代价远高于预防成本。
# .env.example(提交)
DATABASE_URL=mysql://user:pass@localhost:3306/shop
API_KEY=change-me
# .gitignore(追加)
.env
.env.local
.env.*.local按环境拆分文件,并约定合并顺序
为本地、测试、生产分别维护一套变量的方式常见,但别把所有环境的值都堆进一个 .env —— 那等于变相泄露了生产配置给所有开发看到。更稳妥的是 .env(公共)+ .env.local(仅本地覆盖)+ CI secrets(环境专属)的组合。
Node MoE框架的加载顺序要明确。以 Nuxt/Vite 生态为例,约定的顺序通常是 .env < .env.local < .env.<mode>。只要团队内部统一达成的顺序并写进 README,parse 时就按此排序叠加,就能避免"哪个文件优先级高"的口水仗。
生产环境的密钥永远不要写进仓库的 .env 文件;放在 CI 的 secrets 管理或部署平台的加密变量里,构建时注入。
# 约定顺序(写进 README):.env 最弱,最后加载的覆盖
.env # 公共,所有环境
.env.local # 本地覆盖,不入库
.env.staging # 暂存环境(CI 注入)
# Nuxt CLI 会根据 mode 自动加载对应文件验证:变量解析、缺失校验与密钥轮换
上线前做三层验证。第一层解析验证:启动应用,用统一入口打印一次所有依赖的变量名与是否存在的布尔值,确认 .env 真的被加载且当前模式加载对了文件。
第二层缺省校验:在应用启动时遍历必需变量集合,缺失一个就启动失败并给出明确错误,而不是让代码在运行到一半才炸。第三层安全验证:旁路扫描代码与仓库确认无明文密钥,并通过环境变量来源对账保证生产值来自 secrets。
密钥应定期轮换,尤其在有离职或日志泄露时。轮换流程要配合双写过渡:先让新旧密钥都能用,待全员切换后再吊销旧值,避免一次性替换导致线上中断。
# 缺失即失败的校验示例
const REQUIRED = ["DATABASE_URL", "API_KEY"];
for (const k of REQUIRED) {
if (!process.env[k]) throw new Error(`缺少必需的环境变量: ${k}`);
}
console.log("配置校验通过");