config vs dotenv vs dotenv-safe vs env-cmd
JavaScript 环境变量与配置管理方案
configdotenvdotenv-safeenv-cmd类似的npm包:

JavaScript 环境变量与配置管理方案

configdotenvdotenv-safeenv-cmd 都是用于管理应用程序配置和环境变量的 JavaScript 工具,但它们的定位和能力有显著差异。dotenv 是最基础的 .env 文件加载器;dotenv-safe 在其基础上增加了必需变量校验;config 提供了完整的分层配置系统,支持多环境和嵌套结构;而 env-cmd 是一个命令行工具,用于在执行命令前从文件注入环境变量,适用于任何语言环境。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
config1,573,5796,424207 kB141 个月前MIT
dotenv020,34893.3 kB101 个月前BSD-2-Clause
dotenv-safe077110.4 kB42 年前MIT
env-cmd01,81655.2 kB257 个月前MIT

环境变量管理方案深度对比:config vs dotenv vs dotenv-safe vs env-cmd

在现代前端和 Node.js 应用开发中,安全、灵活地管理环境变量是架构设计的基础环节。configdotenvdotenv-safeenv-cmd 是四种广泛采用的方案,但它们的设计哲学、使用场景和能力边界截然不同。本文将从工程实践角度,深入剖析这些工具的核心机制、适用边界与真实权衡。

📦 核心目标与实现方式

dotenv:最轻量的 .env 加载器

dotenv 的唯一职责是读取项目根目录下的 .env 文件,并将其内容注入到 process.env 中。它不提供校验、分层配置或命令行集成,纯粹做“加载”这件事。

// .env
DB_HOST=localhost
API_KEY=secret123

// index.js
require('dotenv').config();
console.log(process.env.DB_HOST); // 'localhost'

注意:dotenv 不会覆盖已存在的环境变量。如果 DB_HOST 已在系统中设置,.env 中的值会被忽略。

dotenv-safe:带强制校验的 dotenv 增强版

dotenv-safedotenv 基础上增加了必需变量校验。它要求你提供一个 .env.example(或自定义名称)文件,列出所有必须存在的变量名。启动时若缺失任一必需变量,程序会直接报错退出。

// .env.example
DB_HOST=
API_KEY=

// .env (缺少 API_KEY)
DB_HOST=localhost

// index.js
require('dotenv-safe').config();
// 抛出错误: Missing environment variables: API_KEY

这种“fail-fast”机制非常适合 CI/CD 或生产环境,避免因配置缺失导致运行时诡异错误。

config:面向复杂应用的分层配置系统

config 不局限于环境变量,它构建了一套完整的分层配置体系。配置来源包括:

  • 默认配置 (config/default.json)
  • 环境特定配置 (config/production.json)
  • 本地覆盖 (config/local.json)
  • 环境变量(需显式映射)
// config/default.json
{
  "db": {
    "host": "localhost",
    "port": 5432
  }
}

// config/production.json
{
  "db": {
    "host": "prod-db.example.com"
  }
}

// index.js
const config = require('config');
console.log(config.get('db.host')); // 生产环境输出 'prod-db.example.com'

环境变量可通过命名规则覆盖配置,例如 export db__port=6432 会覆盖 db.port

env-cmd:通过命令行注入环境变量

env-cmd 本身不是代码库,而是一个 CLI 工具。它允许你在运行命令前,从指定文件加载环境变量,适用于任何语言编写的脚本。

// .env.production
DB_HOST=prod-db.example.com
LOG_LEVEL=error
# package.json
{
  "scripts": {
    "start:prod": "env-cmd -f .env.production node server.js"
  }
}

server.js 中,你可以直接访问 process.env.DB_HOST,无需引入任何依赖。

🔐 安全性与错误预防

变量缺失处理

  • dotenv:静默忽略缺失变量,可能导致 undefined 引发运行时错误。
  • dotenv-safe:启动时严格校验,缺失必填变量立即失败。
  • config:调用 config.get('missing.key') 时抛出异常,但不会在启动时全局校验。
  • env-cmd:无内置校验,行为同原生 process.env
// dotenv-safe 示例:强制校验
require('dotenv-safe').config({
  allowEmptyValues: true, // 允许空值(默认 false)
  example: '.env.required' // 自定义模板文件
});

敏感信息泄露风险

  • config 需特别注意:其配置文件(如 local.json)可能意外提交到 Git。官方建议通过 .gitignore 排除,并使用 custom-environment-variables.json 将敏感字段映射到环境变量。
  • dotenv 系列.env 文件通常加入 .gitignore,但需团队规范约束。
  • env-cmd:配置文件同样需 .gitignore 保护,但因其不嵌入代码,风险略低。

🧩 配置结构与灵活性

扁平 vs 嵌套结构

  • dotenv / dotenv-safe / env-cmd:仅支持扁平键值对(如 DB_HOST),无法直接表达嵌套对象。
  • config:原生支持 JSON/YAML 的嵌套结构,适合复杂配置。
// config 支持嵌套
const port = config.get('server.port');
const timeout = config.get('server.timeout.connect');

若需在 dotenv 中模拟嵌套,需自行解析:

// .env
SERVER_PORT=3000
SERVER_TIMEOUT_CONNECT=5000

// 手动构建对象
const serverConfig = {
  port: process.env.SERVER_PORT,
  timeout: {
    connect: process.env.SERVER_TIMEOUT_CONNECT
  }
};

多环境支持

  • config:通过文件命名约定(default.json, development.json, production.json)自动加载对应环境配置。
  • dotenv 系列:需手动指定文件路径:
// 根据 NODE_ENV 加载不同文件
require('dotenv').config({
  path: `.env.${process.env.NODE_ENV}`
});
  • env-cmd:通过 -f 参数显式指定文件:
env-cmd -f .env.staging npm run start

⚙️ 使用场景与工程实践

场景 1:简单前端构建配置(如 Vite/React)

  • 推荐dotenv
  • 理由:前端构建工具(Vite、Create React App)已内置 dotenv 支持,只需创建 .env 文件即可。无需额外依赖或复杂配置。
# .env
VITE_API_BASE=/api
VITE_APP_NAME=MyApp

注意:Vite 要求变量以 VITE_ 开头才能暴露给客户端代码。

场景 2:Node.js 后端服务(需严格校验)

  • 推荐dotenv-safe
  • 理由:后端服务依赖关键配置(数据库连接、密钥等),缺失会导致服务不可用。dotenv-safe 的启动校验能提前暴露问题。
// server.js
require('dotenv-safe').config({
  example: '.env.example',
  error: '⚠️  缺少必要环境变量,请检查 .env 文件'
});

场景 3:大型企业级应用(多环境、复杂配置)

  • 推荐config
  • 理由:当配置项超过 20 个,且涉及多环境差异、嵌套结构、动态覆盖时,config 的分层模型能显著提升可维护性。
// config/custom-environment-variables.json
{
  "db": {
    "password": "DB_PASSWORD" // 从环境变量 DB_PASSWORD 读取
  }
}

场景 4:跨语言脚本或临时任务

  • 推荐env-cmd
  • 理由:当你需要为 Python 脚本、Shell 命令或非 Node.js 任务注入环境变量时,env-cmd 是通用解决方案。
# 运行 Python 脚本并注入变量
env-cmd -f .env.local python migrate.py

🔄 组合使用策略

在实际项目中,这些工具常被组合使用:

  1. env-cmd + dotenv-safe:在 CI/CD 中通过 env-cmd 注入变量,本地开发用 dotenv-safe 校验。
  2. config + 环境变量config 管理大部分配置,敏感字段通过环境变量注入(通过 custom-environment-variables.json 映射)。

⚠️ 避免反模式:不要同时用 dotenvconfig 加载同一份配置,会导致覆盖逻辑混乱。

📊 总结:核心差异速查表

能力dotenvdotenv-safeconfigenv-cmd
加载 .env 文件✅ (CLI 方式)
必需变量校验❌ (按需校验)
嵌套配置结构
多环境自动切换❌ (需手动)❌ (需手动)❌ (需手动指定)
非 Node.js 适用
是否需代码集成

💡 最终建议

  • 小型项目或前端构建 → 用 dotenv,简单直接。
  • Node.js 后端服务 → 用 dotenv-safe,安全第一。
  • 复杂配置需求 → 用 config,结构清晰。
  • 跨语言或临时任务 → 用 env-cmd,通用灵活。

选择工具的本质是权衡简洁性控制力。没有银弹,只有最适合当前上下文的方案。

如何选择: config vs dotenv vs dotenv-safe vs env-cmd

  • config:

    选择 config 如果你需要管理复杂的、分层的配置结构,支持多环境(开发、测试、生产)自动切换,并且配置项包含嵌套对象。它适合大型企业级应用,但需注意敏感信息不要硬编码在配置文件中。对于简单场景,它可能过于重量级。

  • dotenv:

    选择 dotenv 如果你只需要最基础的 .env 文件加载功能,项目规模小或前端构建工具(如 Vite)已内置支持。它轻量、无依赖,但缺乏校验机制,不适合对配置完整性要求高的后端服务。

  • dotenv-safe:

    选择 dotenv-safe 如果你在 Node.js 后端项目中使用 .env 文件,且需要确保所有必需的环境变量都已提供。它在启动时强制校验,避免因配置缺失导致运行时错误,是 dotenv 的安全增强版。

  • env-cmd:

    选择 env-cmd 如果你需要为非 Node.js 脚本(如 Python、Shell)或临时任务注入环境变量,或者希望在不修改代码的情况下通过命令行切换配置。它作为 CLI 工具使用,不需在代码中引入依赖,但缺乏运行时校验和结构化配置能力。

config的README

Configure your Node.js Applications

npm package Downloads Issues

Release Notes

Introduction

Node-config organizes hierarchical configurations for your app deployments.

It lets you define a set of default parameters, and extend them for different deployment environments (development, qa, staging, production, etc.).

Configurations are stored in configuration files within your application, and can be overridden and extended by environment variables, command line parameters, or external sources.

This gives your application a consistent configuration interface shared among a growing list of npm modules also using node-config.

Project Guidelines

  • Simple - Get started fast
  • Powerful - For multi-node enterprise deployment
  • Flexible - Supporting multiple config file formats
  • Lightweight - Small file and memory footprint
  • Predictable - Well tested foundation for module and app developers

Quick Start

The following examples are in JSON format, but configurations can be in other file formats.

Install in your app directory, and edit the default config file.

$ npm install config
$ mkdir config
$ vi config/default.json
{
  // Customer module configs
  "Customer": {
    "dbConfig": {
      "host": "localhost",
      "port": 5984,
      "dbName": "customers"
    },
    "credit": {
      "initialLimit": 100,
      // Set low for development
      "initialDays": 1
    }
  }
}

Edit config overrides for production deployment:

 $ vi config/production.json
{
  "Customer": {
    "dbConfig": {
      "host": "prod-db-server"
    },
    "credit": {
      "initialDays": 30
    }
  }
}

Use configs in your code:

const config = require('config');
//...
const dbConfig = config.get('Customer.dbConfig');
db.connect(dbConfig, ...);

if (config.has('optionalFeature.detail')) {
  const detail = config.get('optionalFeature.detail');
  //...
}

config.get() will throw an exception for undefined keys to help catch typos and missing values. Use config.has() to test if a configuration value is defined.

Start your app server:

$ export NODE_ENV=production
$ node my-app.js

Running in this configuration, the port and dbName elements of dbConfig will come from the default.json file, and the host element will come from the production.json override file.

TypeScript

Type declarations are published under types/ and resolved via typesVersions. Subpath typings are included for config/async, config/defer, config/parser, config/raw, and config/lib/util in addition to the main config entrypoint.

Articles

Further Information

If you still don't see what you are looking for, here are some more resources to check:

Contributors

lorenwestjdmarshallmarkstosi­Moseselliotttfmdkitzman
jfelegeleachi­M2kjosxenyoleosuncinarthanzel
leonardovillelajeremy-daley-krsimon-scherzingerBadger­Badger­Badger­Badgernsaboviccunneen
Osterjourth507tiny-rac00neheikesfgheorgheroncli
superovenairdrummingfoolwmertensXadilla­Xinsidedsbert

License

May be freely distributed under the MIT license.

Copyright (c) 2010-2026 Loren West and other contributors