Dotenv 环境变量管理完全指南

📌 核心概念

Dotenv - 零依赖模块,将环境变量从 .env 文件加载到 process.env,遵循 The Twelve-Factor App 原则。

📦 安装与版本要求

# 安装最新版本(推荐16.0.0+)
npm install dotenv

# 指定版本
npm install dotenv@16

# yarn
yarn add dotenv

# pnpm
pnpm add dotenv

# 版本建议
# - 新项目:dotenv 16.0.0+(支持override选项)
# - 现有项目:至少使用dotenv 10.0.0+

🚀 基础使用

1. 快速开始

// 方式1:CommonJS - 在入口文件最早处加载
require('dotenv').config();
console.log(process.env.DB_HOST);

// 方式2:ES Module - 推荐控制式加载
import dotenv from 'dotenv';
import path from 'path';

// 根据环境条件加载
if (process.env.NODE_ENV !== 'production') {
  dotenv.config({ 
    path: path.resolve(process.cwd(), '.env.development') 
  });
}

// 方式3:即时执行(不推荐用于生产)
import 'dotenv/config'; // 这会立即执行配置

2. 环境文件示例

# .env 文件示例
DB_HOST=localhost
DB_PORT=5432
DB_USER=admin
DB_PASS="super_secret_password!@#"  # 特殊字符用引号包裹

APP_ENV=development
APP_DEBUG=true
API_KEY=your_api_key_here

# 注释以 # 开头
# 支持空行分隔

# 数组格式(自定义解析)
ALLOWED_ORIGINS= https://localhost:3000 ,https://example.com

# JSON配置(自定义解析)
FEATURE_FLAGS={"newDashboard":true,"darkMode":false}

⚙️ 配置选项详解

config() 方法参数

const result = require('dotenv').config({
  path: '/custom/path/.env',       // 自定义文件路径
  encoding: 'utf8',                // 编码格式(默认utf8)
  debug: process.env.NODE_ENV !== 'production', // 调试模式
  override: false                  // 是否覆盖已存在的环境变量
});

// 返回值结构
console.log(result);
// {
//   parsed: { DB_HOST: 'localhost', ... }, // 解析的变量
//   error: Error | null                     // 错误信息
// }

override 选项重要说明

// ⚠️ 重要行为理解:
// override: false(默认)- 不覆盖已存在的环境变量
// override: true - 强制覆盖

// 示例场景:
// 系统环境变量:PORT=3000(通过shell设置)
// .env 文件:PORT=8080

dotenv.config({ override: false });
console.log(process.env.PORT); // 3000(使用系统变量)

dotenv.config({ override: true });
console.log(process.env.PORT); // 8080(使用.env文件变量)

// 生产环境建议:override: false
// 开发环境建议:override: true

📁 多环境配置策略

推荐的文件结构

├── .env                    # 本地开发环境(不提交到git)
├── .env.example           # 环境变量模板(提交到git)
├── .env.development       # 开发环境配置
├── .env.staging           # 预发布环境配置
├── .env.production        # 生产环境配置
├── .env.test              # 测试环境配置
└── config/
    └── env.js            # 环境配置加载逻辑

自动化智能加载

// config/env-loader.js
const path = require('path');
const dotenv = require('dotenv');
const fs = require('fs');

/**
 * 智能加载环境变量
 * 优先级:.env.local > .env.[NODE_ENV] > .env
 */
function loadEnv() {
  const env = process.env.NODE_ENV || 'development';
  const envFiles = [
    `.env.${env}.local`,
    `.env.${env}`,
    '.env.local',
    '.env'
  ];

  let loadedVars = {};

  envFiles.forEach(file => {
    const envPath = path.resolve(process.cwd(), file);
    if (fs.existsSync(envPath)) {
      const result = dotenv.config({ 
        path: envPath,
        override: env === 'test' // 测试环境强制覆盖
      });
      
      if (result.error) {
        console.warn(`加载 ${file} 失败:`, result.error.message);
      } else if (result.parsed) {
        loadedVars = { ...loadedVars, ...result.parsed };
      }
    }
  });

  console.log(`环境 ${env} 加载完成,加载了 ${Object.keys(loadedVars).length} 个变量`);
  return loadedVars;
}

// 单例模式,避免重复加载
let isEnvLoaded = false;
module.exports = function initEnv() {
  if (!isEnvLoaded) {
    loadEnv();
    isEnvLoaded = true;
  }
  return process.env;
};

🔧 高级用法

1. 类型安全的配置对象

// config/env.js
class EnvConfig {
  constructor() {
    this.loadEnv();
  }

  loadEnv() {
    // 确保环境变量已加载
    if (!process.env.NODE_ENV) {
      require('dotenv').config({ path: '.env' });
    }
  }

  // 应用配置
  get app() {
    return {
      env: process.env.NODE_ENV || 'development',
      port: this.parseInt('PORT', 3000),
      name: process.env.APP_NAME || 'My App',
      debug: this.parseBool('DEBUG', false),
      url: process.env.APP_URL,
    };
  }

  // 数据库配置
  get database() {
    return {
      host: process.env.DB_HOST,
      port: this.parseInt('DB_PORT', 5432),
      name: process.env.DB_NAME,
      user: process.env.DB_USER,
      password: process.env.DB_PASSWORD,
      url: process.env.DATABASE_URL,
      pool: {
        max: this.parseInt('DB_POOL_MAX', 10),
        min: this.parseInt('DB_POOL_MIN', 2),
      }
    };
  }

  // 解析辅助方法
  parseInt(key, defaultValue = 0) {
    const value = process.env[key];
    if (!value) return defaultValue;
    const parsed = parseInt(value, 10);
    return isNaN(parsed) ? defaultValue : parsed;
  }

  parseBool(key, defaultValue = false) {
    const value = process.env[key];
    if (value === undefined || value === null) return defaultValue;
    return value === 'true' || value === '1' || value === 'yes';
  }

  parseArray(key, delimiter = ',', defaultValue = []) {
    const value = process.env[key];
    if (!value) return defaultValue;
    return value.split(delimiter).map(item => item.trim());
  }

  parseJson(key, defaultValue = {}) {
    const value = process.env[key];
    if (!value) return defaultValue;
    try {
      return JSON.parse(value);
    } catch (error) {
      console.warn(`解析JSON环境变量 ${key} 失败:`, error);
      return defaultValue;
    }
  }
}

// 创建单例并冻结
const config = new EnvConfig();
Object.freeze(config);

export default config;

2. 环境变量验证系统

// utils/env-validator.js
class EnvValidator {
  static required(vars, customMessage = null) {
    const missing = vars.filter(v => !process.env[v]);
    
    if (missing.length > 0) {
      const message = customMessage || 
        `缺少必需的环境变量: ${missing.join(', ')}\n请检查您的 .env 文件。`;
      throw new Error(message);
    }
  }

  static validateRules(rules) {
    const errors = [];

    rules.forEach(({ key, required = false, type, pattern, min, max, customValidator }) => {
      const value = process.env[key];
      
      // 检查必需性
      if (required && (value === undefined || value === '')) {
        errors.push(`环境变量 ${key} 是必需的`);
        return;
      }

      // 类型检查
      if (value && type) {
        switch (type) {
          case 'number':
            if (isNaN(Number(value))) errors.push(`${key} 必须是数字`);
            break;
          case 'boolean':
            const lowerValue = value.toLowerCase();
            if (!['true', 'false', '1', '0', 'yes', 'no'].includes(lowerValue)) {
              errors.push(`${key} 必须是布尔值 (true/false)`);
            }
            break;
          case 'url':
            try {
              new URL(value);
            } catch {
              errors.push(`${key} 必须是有效的URL`);
            }
            break;
        }
      }

      // 正则匹配
      if (value && pattern && !pattern.test(value)) {
        errors.push(`${key} 格式不正确`);
      }

      // 范围检查
      if (value && type === 'number') {
        const num = Number(value);
        if (min !== undefined && num < min) errors.push(`${key} 不能小于 ${min}`);
        if (max !== undefined && num > max) errors.push(`${key} 不能大于 ${max}`);
      }

      // 自定义验证器
      if (value && customValidator) {
        const result = customValidator(value, key);
        if (result !== true) errors.push(result);
      }
    });

    if (errors.length > 0) {
      throw new Error(`环境变量验证失败:\n${errors.join('\n')}`);
    }
  }
}

// 使用示例
EnvValidator.required(['DB_HOST', 'API_KEY', 'SECRET_KEY']);

EnvValidator.validateRules([
  { key: 'PORT', type: 'number', min: 1, max: 65535 },
  { key: 'DB_PORT', type: 'number', required: true },
  { key: 'API_URL', type: 'url', required: true },
  { key: 'ADMIN_EMAIL', pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ },
  { 
    key: 'PASSWORD_MIN_LENGTH', 
    type: 'number',
    customValidator: (value) => {
      const len = parseInt(value, 10);
      return len >= 8 ? true : '密码最小长度不能小于8';
    }
  }
]);

3. 动态环境变量扩展

// utils/env-expander.js
class EnvExpander {
  /**
   * 扩展环境变量中的引用
   * 支持 ${VAR} 和 ${VAR:-default} 语法
   */
  static expand(str, maxDepth = 5) {
    let result = str;
    let depth = 0;
    
    while (result.includes('${') && depth < maxDepth) {
      result = result.replace(/\${([^{}]+)}/g, (match, expression) => {
        // 处理默认值语法:${VAR:-default}
        if (expression.includes(':-')) {
          const [varName, defaultValue] = expression.split(':-');
          return process.env[varName] || defaultValue || '';
        }
        
        // 简单变量引用
        return process.env[expression] || '';
      });
      depth++;
    }
    
    // 检查是否达到最大深度(防止无限循环)
    if (depth >= maxDepth && result.includes('${')) {
      console.warn('环境变量扩展达到最大深度,可能存在循环引用');
    }
    
    return result;
  }

  /**
   * 批量扩展所有环境变量
   */
  static expandAll(vars = process.env) {
    const expanded = {};
    
    Object.entries(vars).forEach(([key, value]) => {
      if (typeof value === 'string') {
        expanded[key] = this.expand(value);
      } else {
        expanded[key] = value;
      }
    });
    
    return expanded;
  }
}

// 使用示例
// .env 文件中:
// API_URL=https://${DOMAIN}/api/v1
// DB_URL=postgres://${DB_USER}:${DB_PASS}@${DB_HOST}:${DB_PORT}/${DB_NAME}

const expanded = EnvExpander.expandAll();
console.log(expanded.API_URL); // 已扩展的URL

🛡️ 安全最佳实践

1. 版本控制策略

# .gitignore - 必须排除的文件
.env
.env.local
*.env.*.local
.env.production
.env.staging
secrets/
*.pem
*.key

# 必须提交的文件
.env.example                 # 模板文件
.env.test                    # 测试配置(不含真实密码)

2. 敏感信息保护

// utils/secrets-manager.js
const crypto = require('crypto');

class SecretsManager {
  /**
   * 加密敏感环境变量(开发环境使用)
   */
  static encryptEnvFile(envPath, key) {
    const fs = require('fs');
    const envContent = fs.readFileSync(envPath, 'utf8');
    
    // 提取敏感变量
    const lines = envContent.split('\n');
    const sensitiveVars = [];
    
    lines.forEach(line => {
      if (line.match(/PASSWORD|SECRET|KEY|TOKEN|PRIVATE/i) && !line.startsWith('#')) {
        sensitiveVars.push(line);
      }
    });
    
    if (sensitiveVars.length > 0) {
      console.warn('发现敏感变量,建议使用加密存储:', sensitiveVars);
    }
    
    // 实际项目中,这里应该实现加密逻辑
    // const encrypted = this.encrypt(envContent, key);
    // fs.writeFileSync(`${envPath}.encrypted`, encrypted);
  }

  /**
   * 运行时解密(生产环境使用)
   */
  static async loadEncryptedEnv(encryptedPath, key) {
    // 从加密文件加载
    // const encrypted = fs.readFileSync(encryptedPath);
    // const decrypted = this.decrypt(encrypted, key);
    // 解析并设置到 process.env
    
    // 或者:从云服务加载
    return await this.loadFromCloud();
  }

  /**
   * 从AWS Secrets Manager或Azure Key Vault加载
   */
  static async loadFromCloud() {
    // AWS Secrets Manager 示例
    const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager');
    
    const client = new SecretsManagerClient({
      region: process.env.AWS_REGION,
    });
    
    try {
      const response = await client.send(
        new GetSecretValueCommand({
          SecretId: process.env.AWS_SECRET_ID,
        })
      );
      
      return JSON.parse(response.SecretString);
    } catch (error) {
      console.error('从Secrets Manager加载失败:', error);
      throw error;
    }
  }

  /**
   * 安全日志记录(屏蔽敏感信息)
   */
  static safeLogEnv() {
    const env = { ...process.env };
    const sensitivePatterns = [
      /PASSWORD/i,
      /SECRET/i,
      /KEY/i,
      /TOKEN/i,
      /PRIVATE/i,
      /CREDENTIAL/i,
      /AUTH/i
    ];
    
    Object.keys(env).forEach(key => {
      if (sensitivePatterns.some(pattern => pattern.test(key))) {
        env[key] = '***REDACTED***';
      }
    });
    
    console.log('环境变量(敏感信息已屏蔽):', env);
  }
}

3. 生产环境配置策略

# Docker Compose 示例
version: '3.8'
services:
  app:
    build: .
    env_file:
      - .env.production  # 非敏感配置
    environment:
      - NODE_ENV=production
      - LOG_LEVEL=info
    secrets:
      - db_password
      - api_keys
    # 配置优先级:environment > env_file

secrets:
  db_password:
    external: true  # 从Docker Secrets加载
  api_keys:
    file: ./secrets/api_keys.txt

# Kubernetes Secret 示例
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
data:
  db-password: c3VwZXJfc2VjcmV0X3Bhc3N3b3JkCg==  # base64编码
  api-key: bXlfYXBpX2tleQo=

📊 环境变量类型转换参考

类型 获取方法 示例 注意事项
字符串 process.env.VAR "production" 默认类型
数字 parseInt(process.env.PORT, 10) 3000 指定基数
布尔值 process.env.DEBUG === 'true' true/false 严格比较
数组 process.env.ALLOWED_ORIGINS.split(',') ['a.com','b.com'] 处理空格
JSON JSON.parse(process.env.CONFIG) {key: 'value'} 错误处理
URL new URL(process.env.API_URL) URL对象 验证有效性

🔄 框架特定集成

Next.js

// Next.js 自动加载顺序:
// 1. .env.${NODE_ENV}.local
// 2. .env.local
// 3. .env.${NODE_ENV}
// 4. .env

// 客户端可访问的变量(浏览器端)
NEXT_PUBLIC_API_URL=https://api.example.com
NEXT_PUBLIC_GA_ID=UA-XXXXX-Y

// 仅服务器端访问
DATABASE_URL=postgres://...
SECRET_KEY=your_secret_key

// 访问方式
console.log(process.env.NEXT_PUBLIC_API_URL); // 客户端和服务器
console.log(process.env.DATABASE_URL); // 仅服务器

Vite

// Vite 环境变量必须以 VITE_ 开头才能在客户端访问
VITE_API_KEY=xxx
VITE_API_URL=https://api.example.com

// 服务器端变量(无前缀)
DATABASE_URL=postgres://...

// 访问方式
console.log(import.meta.env.VITE_API_KEY); // 客户端
console.log(process.env.DATABASE_URL);     // 服务器端

// vite.config.js 中加载
import { loadEnv } from 'vite';
export default ({ mode }) => {
  const env = loadEnv(mode, process.cwd(), '');
  return {
    // 配置...
  };
};

Create React App

// 必须使用 REACT_APP_ 前缀
REACT_APP_API_ENDPOINT=https://api.example.com
REACT_APP_DEBUG=true

// 访问方式
console.log(process.env.REACT_APP_API_ENDPOINT);

// 构建时替换
// .env.development 和 .env.production 自动识别

🐛 常见问题与解决方案

问题1:环境变量未加载

// 调试脚本
const dotenv = require('dotenv');
const path = require('path');

console.log('当前目录:', process.cwd());
console.log('NODE_ENV:', process.env.NODE_ENV);

const envPath = path.resolve(process.cwd(), '.env');
console.log('尝试加载:', envPath);

const result = dotenv.config({ 
  path: envPath,
  debug: true 
});

if (result.error) {
  console.error('加载失败:', result.error);
} else {
  console.log('加载成功,变量:', Object.keys(result.parsed || {}));
}

问题2:Windows 兼容性问题

// Windows特殊字符处理
// .env 文件
PASSWORD="abc123!@#"           # 特殊字符用引号
MULTILINE="line1\nline2"       # Windows换行符问题

// 解决方案:使用cross-env设置变量
// package.json
{
  "scripts": {
    "dev:win": "set NODE_ENV=development&& node app.js",
    "dev:unix": "NODE_ENV=development node app.js",
    "dev": "cross-env NODE_ENV=development node app.js"
  }
}

// 或者在代码中规范化
function normalizePath(path) {
  return process.platform === 'win32' 
    ? path.replace(/\\/g, '/')
    : path;
}

问题3:测试环境污染

// jest.setup.js 或测试配置
const dotenv = require('dotenv');

// 每个测试前重置环境
beforeEach(() => {
  jest.resetModules();
  
  // 保存原始环境变量
  global.originalEnv = { ...process.env };
  
  // 加载测试环境
  dotenv.config({ 
    path: '.env.test',
    override: true 
  });
});

afterEach(() => {
  // 恢复原始环境
  if (global.originalEnv) {
    Object.keys(process.env).forEach(key => {
      if (!(key in global.originalEnv)) {
        delete process.env[key];
      }
    });
    
    Object.assign(process.env, global.originalEnv);
  }
});

// 或者在package.json中指定测试环境
// "test": "cross-env NODE_ENV=test jest"

问题4:环境变量大小限制

// ⚠️ 系统限制:单个变量通常最大128KB,总环境空间最大2MB

// 错误示例(可能超过限制)
LARGE_CONFIG={"huge": "json object..."}

// 解决方案
// 1. 拆分成多个变量
CONFIG_PART1={...}
CONFIG_PART2={...}

// 2. 存为文件,环境变量存储文件路径
CONFIG_FILE=/path/to/config.json

// 3. 使用外部配置服务
async function loadLargeConfig() {
  const response = await fetch(process.env.CONFIG_SERVICE_URL);
  return response.json();
}

🎯 推荐工作流

开发环境

# 1. 克隆项目
git clone project && cd project

# 2. 安装依赖
npm install

# 3. 复制环境模板
cp .env.example .env
cp .env.example .env.development

# 4. 编辑配置(使用安全编辑器)
code .env.development

# 5. 启动开发服务器
npm run dev

# 6. 验证环境变量
npm run env:check

CI/CD 管道

# GitHub Actions 示例
name: Deploy
on: [push]

env:
  NODE_ENV: production
  NODE_VERSION: 18

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: ${{ env.NODE_VERSION }}
          
      - name: Install dependencies
        run: npm ci
        
      - name: Load environment variables
        run: |
          echo "DB_URL=${{ secrets.DB_URL }}" >> .env
          echo "API_KEY=${{ secrets.API_KEY }}" >> .env
          
      - name: Run tests
        run: npm test
        
  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to production
        run: |
          # 从AWS Secrets Manager加载生产环境变量
          aws secretsmanager get-secret-value \
            --secret-id production/env \
            --query SecretString \
            --output text > .env.production
          
          # 部署命令
          npm run deploy:prod

生产环境部署

# 方案1:Docker + 环境变量
docker run -d \
  --name myapp \
  -e NODE_ENV=production \
  -e DB_URL="$DB_URL" \
  -e API_KEY="$API_KEY" \
  -p 3000:3000 \
  myapp:latest

# 方案2:Kubernetes ConfigMap + Secret
kubectl create secret generic app-secrets \
  --from-literal=db-password='xxx' \
  --from-literal=api-key='xxx'

kubectl create configmap app-config \
  --from-literal=log-level=info \
  --from-literal=app-name=MyApp

# 方案3:云服务商特定方案
# AWS: Parameter Store + Secrets Manager
# Azure: App Configuration + Key Vault
# GCP: Secret Manager

🔗 相关工具生态系统

工具 用途 安装
dotenv-cli 命令行加载环境变量 npm install -g dotenv-cli
dotenv-expand 支持变量扩展 npm install dotenv-expand
dotenv-safe 验证必需变量 npm install dotenv-safe
envalid 类型安全的环境验证 npm install envalid
convict 配置管理框架 npm install convict
config 多环境配置管理 npm install config
cross-env 跨平台环境变量设置 npm install cross-env
env-cmd 执行命令前加载env文件 npm install env-cmd
dotenv-flow 多环境管理增强 npm install dotenv-flow
@dotenvx/dotenvx 官方扩展工具集 npm install @dotenvx/dotenvx

📝 TypeScript 完整支持

// env.d.ts - 环境变量类型声明
declare namespace NodeJS {
  interface ProcessEnv {
    // 基础配置
    NODE_ENV: 'development' | 'production' | 'test';
    PORT: string;
    HOST: string;
    
    // 数据库
    DB_HOST: string;
    DB_PORT: string;
    DB_USER: string;
    DB_PASSWORD: string;
    DB_NAME: string;
    DATABASE_URL: string;
    
    // 应用配置
    APP_NAME: string;
    APP_VERSION: string;
    LOG_LEVEL: 'debug' | 'info' | 'warn' | 'error';
    
    // 第三方服务
    API_KEY?: string;
    API_SECRET?: string;
    
    // 功能开关
    FEATURE_NEW_UI?: string;
    FEATURE_EXPERIMENTAL?: string;
    
    // 框架特定
    NEXT_PUBLIC_API_URL?: string;     // Next.js
    VITE_API_URL?: string;            // Vite
    REACT_APP_API_URL?: string;       // CRA
  }
}

// 使用示例 - 获得完整类型提示
const config = {
  port: parseInt(process.env.PORT, 10),  // PORT类型为string
  env: process.env.NODE_ENV,             // 枚举类型
  debug: process.env.LOG_LEVEL === 'debug'
};

// 类型安全的配置工厂
class TypedConfig {
  static getRequired<T extends keyof NodeJS.ProcessEnv>(key: T): string {
    const value = process.env[key];
    if (!value) {
      throw new Error(`环境变量 ${key} 未定义`);
    }
    return value;
  }
  
  static getOptional<T extends keyof NodeJS.ProcessEnv>(key: T, defaultValue?: string): string | undefined {
    return process.env[key] || defaultValue;
  }
}

const dbHost = TypedConfig.getRequired('DB_HOST');
const apiKey = TypedConfig.getOptional('API_KEY');

🚨 关键安全警告

必须避免的陷阱

  1. ❌ 永远不要提交 .env 文件到版本控制
  2. ❌ 不要在客户端代码中暴露敏感环境变量
  3. ❌ 避免在日志中记录完整的环境变量
  4. ❌ 不要硬编码回退值作为真实凭证
  5. ❌ 避免使用过于简单的密码或密钥

安全检查清单

📈 性能优化建议

// 1. 避免重复加载
let envCache = null;

function getEnv() {
  if (!envCache) {
    const start = Date.now();
    envCache = dotenv.config();
    console.log(`加载环境变量耗时: ${Date.now() - start}ms`);
  }
  return envCache;
}

// 2. 按需加载
function lazyLoadEnv(section) {
  const sections = {
    db: ['DB_HOST', 'DB_PORT', 'DB_USER', 'DB_PASSWORD'],
    api: ['API_URL', 'API_KEY'],
    app: ['NODE_ENV', 'PORT', 'LOG_LEVEL']
  };
  
  const requiredVars = sections[section] || [];
  requiredVars.forEach(key => {
    if (!process.env[key]) {
      // 加载包含此变量的最小env文件
    }
  });
}

// 3. 预解析常用变量
const preParsedConfig = {
  port: parseInt(process.env.PORT || '3000', 10),
  isProduction: process.env.NODE_ENV === 'production',
  isDebug: process.env.LOG_LEVEL === 'debug'
};

🔄 迁移策略

从硬编码值迁移

// 迁移前
const config = {
  db: {
    host: 'localhost',
    port: 5432,
    user: 'admin',
    password: 'hardcoded_password' // ❌
  }
};

// 迁移步骤
// 1. 创建.env.example文件
// 2. 逐步替换硬编码值为process.env引用
// 3. 添加环境变量验证
// 4. 更新部署文档
// 5. 培训团队成员

// 迁移后
const config = {
  db: {
    host: process.env.DB_HOST,
    port: parseInt(process.env.DB_PORT, 10),
    user: process.env.DB_USER,
    password: process.env.DB_PASSWORD // ✅
  }
};

从其他配置库迁移

// 如果之前使用config库
const oldConfig = require('config');

// 逐步迁移策略
// 1. 创建.env文件对应原有配置
// 2. 创建适配层
class ConfigAdapter {
  static get(key) {
    // 优先使用环境变量,回退到旧配置
    return process.env[key.toUpperCase()] || oldConfig.get(key);
  }
}

// 3. 逐步替换代码中的引用
// 4. 最终移除旧配置库依赖

🎓 总结:架构师视角的最佳实践

核心原则

  1. 安全性优先:敏感信息永远不进入代码仓库
  2. 环境隔离:开发、测试、生产环境完全独立
  3. 可追溯性:环境变更应有记录和审计
  4. 简单性:配置系统不应成为应用的复杂性来源
  5. 可观测性:能够监控和告警配置问题

12-Factor App 合规性

  • ✅ III. 配置:在环境中存储配置
  • ✅ IV. 后端服务:通过配置连接
  • ✅ V. 构建、发布、运行:严格分离
  • ✅ VI. 进程:无状态,共享 nothing

现代架构建议

// 对于大型分布式系统
const Architecture = {
  // 第一层:本地.env文件(开发用)
  LocalEnv: '.env*',
  
  // 第二层:配置服务(生产用)
  ConfigService: {
    AWS: 'Parameter Store + Secrets Manager',
    Azure: 'App Configuration + Key Vault',
    HashiCorp: 'Consul + Vault',
    SelfHosted: 'etcd + confd'
  },
  
  // 第三层:功能标志服务
  FeatureFlags: {
    LaunchDarkly: '商业方案',
    Unleash: '开源方案',
    Flagsmith: '自托管方案'
  },
  
  // 监控层
  Monitoring: {
    Validation: '启动时验证所有配置',
    ChangeDetection: '配置变更监控',
    Rollback: '配置回滚机制'
  }
};

未来趋势

  1. GitOps 配置管理:环境变量作为代码管理
  2. 动态配置:运行时无需重启更新配置
  3. 配置即服务:集中式配置管理中心
  4. AI辅助优化:智能配置推荐和验证

📋 快速参考卡片

紧急情况处理

# 1. 环境变量泄露
# 立即:轮换所有泄露的密钥
# 然后:审计访问日志,更新.env文件

# 2. 配置错误导致服务宕机
# 使用:配置版本管理,快速回滚

# 3. 缺少环境变量
# 检查:启动时验证脚本是否运行
# 修复:更新部署脚本或CI/CD配置

新项目快速开始

# 初始化环境配置
mkdir config
touch .env.example
touch .env.development
touch .env.test

# 安装核心依赖
npm install dotenv
npm install -D @types/node  # TypeScript项目

# 创建配置加载器
# 参考本文的 config/env-loader.js

posted @ 2026-01-04 22:13  转角90  阅读(213)  评论(0)    收藏  举报