config vs dotenv vs dotenv-safe vs envalid
前端项目中的环境变量与配置管理方案对比
configdotenvdotenv-safeenvalid类似的npm包:

前端项目中的环境变量与配置管理方案对比

configdotenvdotenv-safeenvalid 都是用于管理应用程序配置和环境变量的 Node.js 工具,但它们的设计理念和适用场景有明显差异。dotenv 是最基础的工具,用于从 .env 文件加载环境变量到 process.envdotenv-safe 在其基础上增加了对必需变量的校验;envalid 则提供了一套完整的类型安全、验证和默认值机制,强调运行时配置的可靠性;而 config 采用基于文件的多环境配置方案,适合复杂企业级应用,通过 JSON/YAML/JS 等格式组织不同环境的配置文件。这些工具帮助开发者在不同部署环境中安全、清晰地管理配置,避免硬编码敏感信息或逻辑分支。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
config06,424207 kB141 个月前MIT
dotenv020,34693.3 kB91 个月前BSD-2-Clause
dotenv-safe077110.4 kB42 年前MIT
envalid01,55184.4 kB125 个月前MIT

前端项目中的环境变量与配置管理:config vs dotenv vs dotenv-safe vs envalid

在现代 JavaScript 应用中,如何安全、可靠地管理配置和环境变量是一个基础但关键的问题。configdotenvdotenv-safeenvalid 提供了四种截然不同的思路。本文将从实际工程角度,深入比较它们的核心机制、适用边界和代码实践。

📁 配置来源与组织方式

config 使用基于文件的配置体系,按环境划分配置文件。

  • 默认从 config/ 目录加载 default.json,再根据 NODE_ENV 覆盖(如 production.json)。
  • 支持 JSON、YAML、JS 等多种格式,适合结构化、分层的配置。
// config/default.json
{
  "db": {
    "host": "localhost",
    "port": 5432
  },
  "apiTimeout": 5000
}

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

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

dotenv.env 文件读取键值对,注入到 process.env

  • 文件格式简单,仅支持扁平键值对,不支持嵌套结构。
  • 所有值均为字符串,需手动转换类型。
# .env
DB_HOST=localhost
DB_PORT=5432
API_TIMEOUT=5000
// app.js
require('dotenv').config();
console.log(process.env.DB_HOST); // 'localhost'
console.log(typeof process.env.DB_PORT); // 'string' — 注意!

dotenv-safedotenv 基础上,要求提供 .env.example 作为必需变量模板。

  • 启动时校验 .env 是否包含 .env.example 中定义的所有变量。
  • 仍使用 process.env,但提供更强的存在性保证。
# .env.example
DB_HOST=
DB_PORT=
API_TIMEOUT=
// app.js
require('dotenv-safe').config();
// 若 .env 缺少 DB_HOST,程序会抛出错误并退出

envalid 要求显式声明每个环境变量的规范,生成一个干净的配置对象。

  • 不污染 process.env,而是返回一个经过验证和类型转换的对象。
  • 支持嵌套结构(通过组合),但推荐扁平化设计。
// config.js
const { cleanEnv, str, num } = require('envalid');

const env = cleanEnv(process.env, {
  DB_HOST: str(),
  DB_PORT: num(),
  API_TIMEOUT: num({ default: 5000 })
});

module.exports = env;
// app.js
const env = require('./config');
console.log(env.DB_HOST); // 'localhost'
console.log(typeof env.DB_PORT); // 'number' — 自动转换!

🔍 验证与类型安全

config 本身不提供任何验证或类型转换。

  • 开发者需自行确保配置文件内容正确,容易因拼写错误或类型不符导致运行时异常。
  • 可通过自定义 JS 配置文件加入验证逻辑,但非内置能力。
// config/custom-environment-variables.js
// 仅支持将 process.env 映射到 config 结构,无验证
module.exports = {
  db: {
    host: 'DB_HOST', // 从 process.env.DB_HOST 读取
    port: 'DB_PORT'
  }
};

dotenv 完全无验证。

  • 即使 .env 文件缺失或变量未定义,也不会报错,可能导致后续逻辑崩溃。

dotenv-safe 仅验证变量是否存在,不验证类型或格式。

  • 例如,若 DB_PORT=abc,它不会报错,但后续 parseInt(process.env.DB_PORT) 会得到 NaN

envalid 提供全面的运行时验证和类型转换。

  • 内置 strnumboolurlemail 等验证器。
  • 支持自定义验证函数,错误信息清晰明确。
const { cleanEnv, str, url } = require('envalid');

const env = cleanEnv(process.env, {
  API_BASE_URL: url(), // 必须是有效 URL
  LOG_LEVEL: str({ choices: ['debug', 'info', 'error'] })
});

// 若 API_BASE_URL='not-a-url',启动时抛出:
// EnvVarError: Invalid URL for API_BASE_URL: not-a-url

🧪 默认值与可选性

config 通过 default.json 提供默认值,环境特定文件可覆盖。

  • 但无法表达“某些变量在特定环境下可选”的语义。

dotenv 无内置默认值机制。

  • 需在代码中手动处理:const port = process.env.PORT || 3000;

dotenv-safe 同样无默认值支持。

  • .env.example 中列出的变量必须全部存在,无法标记为可选。

envalid 显式支持默认值和可选变量。

  • 通过 default 选项提供回退值,未提供且无默认值的变量视为必需。
const env = cleanEnv(process.env, {
  PORT: num({ default: 3000 }), // 可选,有默认值
  DATABASE_URL: str() // 必需
});

🛡️ 安全性与错误处理

config 在配置缺失时可能静默返回 undefined,导致延迟错误。

  • 例如 config.get('non.existent') 返回 undefined,直到使用时才报错。

dotenvdotenv-safe 的错误通常发生在应用逻辑层,而非配置加载层。

  • 这使得调试困难,尤其在大型项目中。

envalid 坚持“快速失败”原则。

  • 所有验证在应用启动初期完成,错误信息包含变量名、期望类型和实际值,极大缩短调试路径。
  • 生成的配置对象是不可变的(通过 Object.freeze),防止意外修改。

🧩 与前端构建工具集成

在前端项目(如 React、Vue)中,通常需要将环境变量内联到构建产物中。

  • dotenv 被 Create React App、Vite 等工具链原生支持(自动加载 .env 文件)。
  • envalid 可在构建前的 Node.js 脚本中使用,验证后将结果写入临时文件供构建工具读取。
  • configdotenv-safe 在纯前端项目中较少直接使用,因其依赖 Node.js 文件系统。

例如,在 Vite 项目中结合 envalid

// scripts/validate-env.js
const fs = require('fs');
const { cleanEnv, str } = require('envalid');

const env = cleanEnv(process.env, {
  VITE_API_URL: str()
});

// 将验证后的变量写入 vite.config.js 可读取的位置
fs.writeFileSync(
  './src/env-config.js',
  `export const envConfig = ${JSON.stringify(env)};`
);
// vite.config.js
import { defineConfig } from 'vite';
import { envConfig } from './src/env-config.js';

export default defineConfig({
  define: {
    '__APP_CONFIG__': JSON.stringify(envConfig)
  }
});

📊 总结:核心差异对比

特性configdotenvdotenv-safeenvalid
配置来源多文件(JSON/YAML/JS).env 文件.env + .env.exampleprocess.env(显式声明)
类型转换✅(自动)
验证能力存在性检查✅(类型、格式、自定义)
默认值通过 default.json手动处理✅(声明式)
错误时机延迟(使用时)延迟启动时(仅存在性)启动时(全面)
前端友好度低(需 Node.js)高(工具链集成)中(需构建脚本)

💡 选型建议

  • 小型前端项目:直接使用构建工具内置的 dotenv 支持即可,保持简单。
  • 需要确保关键变量存在:升级到 dotenv-safe,防止部署遗漏。
  • 中大型全栈应用,配置复杂:考虑 config,利用其多环境文件组织能力。
  • 追求高可靠性、类型安全和清晰契约:选择 envalid,尤其适合 TypeScript 项目。

最终,没有“最好”的工具,只有“最合适”当前项目规模、团队习惯和可靠性要求的方案。

如何选择: config vs dotenv vs dotenv-safe vs envalid

  • config:

    选择 config 如果你的项目需要支持多个复杂环境(如 dev、test、staging、prod),且每个环境有大量结构化配置项。它通过文件系统组织配置,天然支持继承和覆盖,适合大型后端服务或全栈应用。但要注意它不直接处理 .env 文件,也不提供类型验证,需配合其他机制确保配置正确性。

  • dotenv:

    选择 dotenv 如果你只需要简单地从 .env 文件加载键值对到 process.env,且项目对配置验证要求不高。它是生态中最轻量、使用最广泛的方案,适合小型项目或作为其他配置系统的底层依赖。但缺乏对缺失变量或类型错误的防护,生产环境需额外保障。

  • dotenv-safe:

    选择 dotenv-safe 如果你希望在 dotenv 的基础上增加对必需环境变量的检查,防止因遗漏关键配置导致运行时错误。它通过 .env.example 定义模板,启动时校验所有必需变量是否存在。适合中等规模项目,追求比 dotenv 更强的安全性,又不想引入复杂验证逻辑的场景。

  • envalid:

    选择 envalid 如果你重视类型安全、明确的配置契约和早期失败原则。它强制定义每个环境变量的类型、默认值和验证规则,并在启动时抛出清晰错误。生成的配置对象具有 TypeScript 类型推断支持,极大减少运行时错误。适合对可靠性和可维护性要求高的现代前端或全栈项目。

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