
大家好,今天想和大家讨论和分享一下我对 web 项目里 NODE_ENV 环境变量的看法。
在很多 web 项目里,环境变量的设计往往是一个“看起来简单,但实际非常容易踩坑”的问题。
最典型的就是:
NODE_ENV 到底应该怎么用? dev / test / staging / prod 要不要塞进 NODE_ENV?
这几年来我经历过各种大小草台班子,接手过各种 web 项目,看到很多团队图省事,会把所有环境都塞进 NODE_ENV,比如:
NODE_ENV=dev
NODE_ENV=test
NODE_ENV=uat
NODE_ENV=staging
NODE_ENV=prod
这种方式前期看似乎没问题,但随着项目变复杂,需求、环境变多,参与人数变多等等,这种设计基本都会逐渐变成隐患。
今天分享这篇文章,主要想和大家分享一下,我们得搞清楚一下几点:
NODE_ENV 是 运行模式。
很多测试框架会强制设 NODE_ENV=test
当我们在项目里需要判定当前逻辑所处的运行模式是什么的时候,记住:在 Web 开发中, NODE_ENV 其实有着非常明确的约定:
| 值 | 含义 |
|---|---|
| development | 开发模式 |
| production | 生产模式 |
很多框架和库都会根据 NODE_ENV 自动设定一些行为,比如:
如果你把 NODE_ENV 改成:
NODE_ENV=uat
NODE_ENV=staging
这些库很可能识别不了,从而导致行为异常。
我曾经把 NODE_ENV 当成”当前代码运行在哪一个环节“的判定依据:
if (process.env.NODE_ENV === 'dev') {}
if (process.env.NODE_ENV === 'uat') {}
if (process.env.NODE_ENV === 'staging') {}
这里其实混淆了两个基本概念:
或许我们不应该用同一个变量来表示这两种概念。
对吧?运行模式的关注点在于代码如何运行,我们根据这一点来控制是否调试、是否输出细节,启用 source map,运行模式会影响打包结果,并且应该进入构建产物内部,我不在乎这份代码构建后在什么环境下运行。
然而部署环境就不一样了,它的核心关注点是:代码运行在哪里?
举个例子,假设代码运行在 test 环境下,我们正在对代码进行测试,难道这时候你想要让类似 Sentry 这样的第三方服务接入进来吗?
当然不需要!就算是免费的服务,也不需要在测试的时候接进来啊,白嫖也不行!
我们需要用一个变量来标识部署的环境,也可以说是运行的环境。
但现实是很多团队、项目会一把梭 NODE_ENV,某种意义上来说,这个变量传播最早也最广,大家习惯了用它控制一切。
并且,一个变量就能决定“开发时友好,生产时高效”,确实方便。
但我们不能忽略这样做的代价:
在前端项目中,NODE_ENV 主要影响构建结果(Vite/Webpack);在后端(如 Node.js/NestJS/Express)中,更建议用 APP_ENV 控制业务配置。
| 变量 | 作用 |
|---|---|
| NODE_ENV | 控制程序运行模式 |
| APP_ENV | 控制部署环境 |
NODE_ENV:只保留给构建工具使用(vite/rollup 等),严格只有 development / production 两个值。
APP_ENV 或 DEPLOY_ENV:真正代表部署环境(dev/test/staging/prod)。
Staging 环境的定位:
如果不分开(只用一个变量,比如 NODE_ENV 或 ENV),可能有人会这样写:
# 很多人会这么写
ENV=staging
# 或者
NODE_ENV=staging
然后代码里这样判断:
if (process.env.NODE_ENV === 'production') {
// 生产行为:压缩、关闭调试、无彩色日志等
} else {
// 开发行为:详细日志
// source map、React DevTools 等
}
在 staging 环境下会出现以下问题:
想用 production mode 的优化,但环境变量是 staging。
结果是用了 development 模式:代码没压缩、日志超级详细、source map 没去掉、性能比生产差很多,无法准确验证性能和稳
定性。
想用 development mode 方便调试,但又怕污染生产行为。
如果强行设 NODE_ENV=development,那整个 staging 就失去了“类生产”的意义,测出来的结果不可信。
配置冲突:数据库要用 staging 专属的库(不能连 prod DB)
支付、短信、推送等第三方服务要用测试账号,但日志、监控、告警又希望和生产保持一致(格式、级别、压缩方式)
最终结果:要么 staging 测不准,要么调试很便秘,花费的时间大大增加,然后?导致加班 ?🤣
现在就以这个为例,我们试试看拆开成两个变量:
# 运行模式(决定代码如何运行、打包结果)
NODE_ENV=production
# 或 APP_MODE=production
# 部署环境(决定连哪个数据库、用什么密钥、开哪些功能)
# dev | test | staging | prod
APP_ENV=staging
其他场景下,NODE_ENV 基本都设置为 production!部署环境则根据 test、staging、prod 来切换逻辑。
在这个 Staging 环境下,运行模式是 production,构建后:
同时,部署环境是 staging:
代码示例:
const mode = process.env.NODE_ENV; // production
const env = process.env.APP_ENV; // staging
// 构建/运行时行为
const isProdMode = mode === 'production';
// 业务和基础设施配置
const config = {
db: {
host: env === 'prod' ? 'prod-db' : `${env}-db`,
},
logging: {
level: isProdMode ? 'info' : 'debug',
pretty: !isProdMode,
},
features: featureFlags[env], // staging 可以有独立的 feature flags
};
甚至配置文件也可以按 APP_ENV 拆分:
这样一来我们就可以去做一次性构建,多环境部署。
在 CI/CD 的过程中,只需要构建一次代码,再部署到多个环境即可。多环境下多次重复构建就显得有点浪费时间了。
以下几点,我将之写入了自己所有项目的 README 中
如果你正在做一个中大型后端系统,这一条设计,值得你在一开始就定对。否则后面重构成本会非常高。
如果你已经在用 NODE_ENV=dev/staging/prod,也可以逐步引入 APP_ENV,把职责拆清楚,就能平滑过渡。
最近 AI 的发展实在是太快了,很多人已经完全不再编写代码,Vibe coding 堪比许愿,在 Agent 帮助下,连自己review 都省了。
但是,我还是觉得 AI 只是拓展开发者能力的工具,至少目前还是!我们可以借助 AI 来做很多原本自身做不到的事情,或者需要花很多时间精力才能做到的事情。但是在开发这件事情上,一切的根基还是我们自己。
感谢大家看到这里,如果你喜欢我分享的内容,欢迎给我三连支持,你的支持是我更新下去的动力!
下次见,Bye 👋🏻
