npm package: @qjmyrs/webpack-plugin-runtime-env
Description: Webpack 插件：支持运行时环境配置
Author: qjmyrs
Latest version: 1.0.1

# @company/webpack-plugin-runtime-env

Webpack 插件，支持在单一构建产物中包含多环境配置，运行时动态选择。

## 特性

- ✅ 完全兼容现有 `.env`、`.env.production` 等配置文件
- ✅ 不改变现有代码，继续使用 `process.env.VUE_APP_XXX`
- ✅ 一次构建，部署到所有环境
- ✅ 支持自定义环境检测逻辑
- ✅ 支持 Webpack 4 和 5

## 安装

```bash
npm install @company/webpack-plugin-runtime-env --save-dev
```

## 使用

### Vue CLI 项目配置

```javascript
// vue.config.js
const WebpackRuntimeEnvPlugin = require('@company/webpack-plugin-runtime-env');

module.exports = {
  configureWebpack: {
    plugins: [
      new WebpackRuntimeEnvPlugin({
        // 项目根目录，默认 process.cwd()
        root: __dirname,
        
        // 需要包含的环境列表
        envs: ['development', 'test', 'staging', 'production'],
        
        // 环境变量前缀，Vue CLI 默认是 VUE_APP_
        prefix: 'VUE_APP_',
        
        // 是否启用调试日志
        debug: false
      })
    ]
  }
};
```

### 自定义环境检测

```javascript
// vue.config.js
const WebpackRuntimeEnvPlugin = require('@company/webpack-plugin-runtime-env');

module.exports = {
  configureWebpack: {
    plugins: [
      new WebpackRuntimeEnvPlugin({
        envs: ['development', 'test', 'staging', 'production'],
        prefix: 'VUE_APP_',
        
        // 自定义环境检测函数（字符串形式）
        envDetector: `
          function() {
            // 方案 1: 基于 URL 参数
            var params = new URLSearchParams(window.location.search);
            var envParam = params.get('env');
            if (envParam) return envParam;
            
            // 方案 2: 基于 localStorage（用于本地调试）
            var localEnv = localStorage.getItem('DEBUG_ENV');
            if (localEnv) return localEnv;
            
            // 方案 3: 基于域名（默认）
            var hostname = window.location.hostname;
            if (hostname.includes('test')) return 'test';
            if (hostname.includes('staging')) return 'staging';
            if (hostname === 'localhost') return 'development';
            
            return 'production';
          }
        `
      })
    ]
  }
};
```

## 配置选项

| 选项 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `root` | `string` | `process.cwd()` | 项目根目录 |
| `envs` | `string[]` | `['development', 'test', 'staging', 'production']` | 要读取的环境列表 |
| `envFilePattern` | `string` | `'.env'` | 环境文件命名模式 |
| `envKey` | `string` | `'__RUNTIME_ENV__'` | 注入到 window 的键名 |
| `prefix` | `string` | `'VUE_APP_'` | 环境变量前缀 |
| `includeBase` | `boolean` | `true` | 是否包含基础 .env 文件 |
| `envDetector` | `string\|function` | 默认检测器 | 自定义环境检测函数 |
| `debug` | `boolean` | `false` | 是否输出调试信息 |

## 现有项目配置示例

假设你的项目有以下配置文件：

```bash
.env                    # 基础配置
.env.development        # 开发环境
.env.test              # 测试环境
.env.staging           # 预发环境
.env.production        # 生产环境
```

```bash
# .env
VUE_APP_TITLE=My App

# .env.development
VUE_APP_API_URL=http://localhost:3000
VUE_APP_ENV=development

# .env.test
VUE_APP_API_URL=https://api-test.company.com
VUE_APP_ENV=test

# .env.staging
VUE_APP_API_URL=https://api-staging.company.com
VUE_APP_ENV=staging

# .env.production
VUE_APP_API_URL=https://api.company.com
VUE_APP_ENV=production
```

配置插件后，原有代码**无需修改**：

```javascript
// 原有代码
const apiUrl = process.env.VUE_APP_API_URL;
console.log(apiUrl); // 根据运行时环境自动切换
```

**构建后自动转换为**：

```javascript
// 编译后的代码
const apiUrl = (window.__RUNTIME_ENV__.getConfig().VUE_APP_API_URL);
console.log(apiUrl); // 根据运行时环境自动切换
```

## 代码转换示例

插件会在编译时自动将 `process.env` 的访问替换为 `window.__RUNTIME_ENV__.getConfig()`：

**源代码：**
```javascript
// 1. 直接访问
const apiUrl = process.env.VUE_APP_API_URL;

// 2. 在对象中使用
const config = {
  apiUrl: process.env.VUE_APP_API_URL,
  timeout: process.env.VUE_APP_TIMEOUT
};

// 3. 在函数中使用
function getApiUrl() {
  return process.env.VUE_APP_API_URL;
}

// 4. 在模板字符串中使用
const url = `${process.env.VUE_APP_API_URL}/users`;

// 5. 条件判断
if (process.env.VUE_APP_ENV === 'production') {
  console.log('生产环境');
}
```

**编译后：**
```javascript
// 1. 直接访问
const apiUrl = (window.__RUNTIME_ENV__.getConfig().VUE_APP_API_URL);

// 2. 在对象中使用
const config = {
  apiUrl: (window.__RUNTIME_ENV__.getConfig().VUE_APP_API_URL),
  timeout: (window.__RUNTIME_ENV__.getConfig().VUE_APP_TIMEOUT)
};

// 3. 在函数中使用
function getApiUrl() {
  return (window.__RUNTIME_ENV__.getConfig().VUE_APP_API_URL);
}

// 4. 在模板字符串中使用
const url = `${(window.__RUNTIME_ENV__.getConfig().VUE_APP_API_URL)}/users`;

// 5. 条件判断
if ((window.__RUNTIME_ENV__.getConfig().VUE_APP_ENV) === 'production') {
  console.log('生产环境');
}
```

## 高级用法

### 在应用中手动切换环境（用于调试）

```javascript
// 查看所有可用环境
console.log(window.__RUNTIME_ENV__);

// 查看当前环境
console.log(window.__RUNTIME_ENV__.getCurrentEnv());

// 手动切换到测试环境（用于调试）
window.__RUNTIME_ENV__.setEnv('test');

// 刷新页面后生效
location.reload();
```

### 配合 @company/runtime-env-core 使用

```javascript
import { initRuntimeEnv, getEnvValue } from '@company/runtime-env-core';

// 初始化
initRuntimeEnv();

// 获取配置
const apiUrl = getEnvValue('VUE_APP_API_URL');
```

## 工作原理

1. **构建时**：
   - 插件读取所有 `.env.*` 文件，将所有环境的配置打包到 `runtime-env.js`
   - 类似 webpack DefinePlugin，将代码中的 `process.env.VUE_APP_XXX` 替换为 `window.__RUNTIME_ENV__.getConfig().VUE_APP_XXX`
2. **运行时**：
   - 根据环境检测器（默认基于域名）选择对应环境的配置
   - `window.__RUNTIME_ENV__.getConfig()` 返回当前环境的配置对象

## 注意事项

1. ⚠️ 所有环境的配置都会打包到前端代码中，**不要在 .env 中存放敏感信息**
2. ⚠️ 构建时只需执行一次 `npm run build`，无需指定环境
3. ✅ 部署时不需要任何特殊处理，直接部署到对应环境即可
4. ✅ 插件会在编译时自动替换 `process.env.XXX`，无需担心运行时性能问题
5. ⚠️ 只会替换符合 `prefix` 前缀的环境变量（默认 `VUE_APP_`）
6. ✅ 可以通过 `debug: true` 查看详细的替换日志

## License

MIT