环境变量里面的.env文件是如何起作用的?

57 阅读3分钟

1. 配置文件的加载机制:

让我们看看代码中是如何处理这些配置文件的:

// src/config/index.js 中的代码
const loadEnv = () => {
  const env = process.env.NODE_ENV || 'development';  // 获取当前环境
  const envPath = path.resolve(process.cwd(), `.env.${env}`);  // 根据环境选择配置文件
  
  dotenv.config({ path: envPath });  // 加载对应的配置文件
};

通过path.resolve(process.cwd(),.env.${env}); 获取到环境变量的文件,然后读取信息。

2. 环境切换机制:

在 package.json 中定义了不同环境的启动命令:

{
  "scripts": {
    "dev": "cross-env NODE_ENV=development nodemon src/app.js",    // 开发环境
    "start": "cross-env NODE_ENV=production node src/app.js"       // 生产环境
  }
}

工作流程:

  1. 开发环境(执行 npm run dev):
  • 设置 NODE_ENV=development
  • 加载 .env.development 文件
  • 使用 nodemon 启动服务(支持热重载)
  • 输出详细的调试信息
  • 使用本地数据库配置
  1. 生产环境(执行 npm start):
  • 设置 NODE_ENV=production
  • 加载 .env.production 文件
  • 使用 node 直接启动服务
  • 只输出重要的日志信息
  • 使用生产环境数据库配置

3. 配置文件的使用:

在代码中通过 process.env 来使用这些配置,例如:

// src/config/db.js 中的数据库配置
const sequelize = new Sequelize({
  dialect: 'mysql',
  host: process.env.DB_HOST,        // 使用配置文件中的数据库主机
  port: process.env.DB_PORT,        // 使用配置文件中的数据库端口
  database: process.env.DB_NAME,    // 使用配置文件中的数据库名
  username: process.env.DB_USER,    // 使用配置文件中的用户名
  password: process.env.DB_PASSWORD // 使用配置文件中的密码
});

// src/app.js 中的跨域配置
app.use(cors({
  origin: process.env.CORS_ORIGIN,  // 使用配置文件中的跨域配置
  credentials: true
}));
  1. 安全考虑:
  • 这两个文件都在 .gitignore 中,不会被提交到代码仓库
  • 生产环境的敏感信息(如密码、密钥)应该通过安全的方式管理
  • 配置文件中的值可以被环境变量覆盖

使用示例:

# 开发环境启动
npm run dev
# 此时会加载 .env.development 的配置
# 使用 nodemon 启动,支持热重载

# 生产环境启动
npm start
# 此时会加载 .env.production 的配置
# 使用 node 直接启动,不支持热重载

问题1:为什么.env.production文件和.env.development文件放在根目录下,src/config/index.js里面的文件通过下面代码就能获取到环境变量。但是如果你将.env.production文件和.env.development文件放到src/config目录里面就获取不到了?

  const env = process.env.NODE_ENV || 'development';
  const envPath = path.resolve(process.cwd(), `.env.${env}`);

回答:

  1. process.cwd() 返回 Node.js 进程的当前工作目录,也就是我们运行 npm run dev 的目录(bpm-server 目录),此时npm run dev命令指向那个地址,它本身就会去那个根目录下面找对应的文件。
  1. path.resolve() 方法会将路径片段解析为绝对路径:
  • 第一个参数 process.cwd() 获取当前工作目录
  • 第二个参数 .env.${env} 是要查找的文件名
  • 当 NODE_ENV=development 时,就会变成 .env.development

问题2.如果我把.env.production文件和.env.development文件放到src/config/目录下面,那么src/config/index.js这个文件该怎么获取到这两个文件呢?

有2个办法,1使用路径搜索,意思就是我写一段代码,要他去搜索我项目里面哪里有.env.production文件和.env.development文件,搜索到了以后,我在去设置。

const path = require('path');
const dotenv = require('dotenv');
const fs = require('fs');

const loadEnv = () => {
  const env = process.env.NODE_ENV || 'development';
  
  // 定义可能的环境文件路径
  const possiblePaths = [
    path.resolve(process.cwd(), `.env.${env}`),           // 项目根目录
    path.resolve(__dirname, `.env.${env}`),               // 当前配置文件目录
    path.resolve(process.cwd(), 'src/config', `.env.${env}`) // src/config 目录
  ];

  // 查找第一个存在的环境文件
  let envPath = null;
  for (const p of possiblePaths) {
    console.log(`- 检查: ${p}`);
    if (fs.existsSync(p)) {
      envPath = p;
      console.log(`✓ 找到环境文件: ${p}`);
      break;
    }
  }

  if (!envPath) {
    console.log('❌ 警告:未找到环境配置文件!');
    return;
  }

  console.log('===================\n');

  // 加载找到的环境文件
  dotenv.config({ path: envPath });
};

module.exports = {
  loadEnv
};

还有一个办法就是:直接使用 __dirname 指定配置文件目录

const path = require('path');
const dotenv = require('dotenv');
const fs = require('fs');

const loadEnv = () => {
  const env = process.env.NODE_ENV || 'development';
  // 使用 __dirname 直接指向当前文件所在目录
  const envPath = path.resolve(__dirname, `.env.${env}`);

  console.log('\n=== 环境文件信息 ===');
  console.log('配置文件目录:', __dirname);
  console.log('环境变量文件:', envPath);
  
  if (!fs.existsSync(envPath)) {
    console.log('❌ 警告:环境配置文件不存在!');
    return;
  }
  
  console.log('✓ 配置文件已找到');
  console.log('===================\n');

  dotenv.config({ path: envPath });
};

module.exports = {
  loadEnv
};

如果是我肯定选择方法2,.env.production文件和.env.development文件放在哪里我一目了然,为什么要花费代码去搜索呢?