1. 为什么需要多环境配置?

刚开始接触Vue项目时,我也觉得环境配置是个可有可无的东西。直到有一次,我在本地开发时调用的API地址忘记切换,直接把测试环境的代码发布到了线上,导致用户看到了一堆测试数据。那次事故让我深刻认识到,环境配置不是锦上添花,而是项目开发的必需品。

在真实项目中,我们通常会遇到四种典型环境:

  • 开发环境(dev):本地开发调试用,可能需要mock数据或连接开发服务器
  • 测试环境(test):QA团队验证功能,需要独立的数据源
  • 预发布环境(staging):尽可能模拟生产环境,用于最终验证
  • 生产环境(prod):真实线上环境,直接影响终端用户

每个环境的配置差异可能包括:

  • API接口地址
  • 第三方SDK密钥
  • 日志级别
  • 功能开关
  • 分析统计ID

手动维护这些配置不仅容易出错,还会让代码库充满各种if-else判断。这就是为什么我们需要建立一套规范的环境管理体系。

2. 环境变量的基础实现

2.1 process.env的工作原理

process.env是Node.js环境下的全局变量,它包含了当前进程的所有环境变量信息。在Vue CLI创建的项目中,webpack会在构建时将这些环境变量静态替换到客户端代码中。

这里有个关键点需要注意:只有以VUE_APP_开头的变量才会被webpack打包进客户端代码。这是出于安全考虑,避免意外泄露敏感信息。

// 正确的定义方式
VUE_APP_API_URL=https://api.dev.example.com

// 错误的定义方式(不会被打包)
SECRET_KEY=123456

2.2 .env文件的使用规范

在项目根目录下,我们可以创建多个.env文件来管理不同环境的配置:

.env                # 在所有环境中共享的默认值
.env.local          # 本地覆盖配置,应该加入.gitignore
.env.development    # 开发环境专用
.env.production     # 生产环境专用
.env.test           # 测试环境专用

文件加载的优先级很有意思:

  1. .env.local
  2. .env.[mode]
  3. .env

这意味着你可以在.env中设置通用配置,在特定环境文件中覆盖个别值。比如:

# .env
VUE_APP_API_VERSION=v1
VUE_APP_LOG_LEVEL=debug

# .env.production
VUE_APP_LOG_LEVEL=error

2.3 实际配置示例

让我们看一个完整的配置案例:

# .env.development
NODE_ENV=development
VUE_APP_BASE_API=https://dev-api.example.com
VUE_APP_CDN_URL=https://dev-cdn.example.com
VUE_APP_GA_ID=UA-XXXXX-1
VUE_APP_DEBUG=true

# .env.production
NODE_ENV=production
VUE_APP_BASE_API=https://api.example.com
VUE_APP_CDN_URL=https://cdn.example.com
VUE_APP_GA_ID=UA-XXXXX-2
VUE_APP_DEBUG=false

在package.json中配置对应的启动命令:

{
  "scripts": {
    "serve": "vue-cli-service serve --mode development",
    "build": "vue-cli-service build --mode production",
    "build:stage": "vue-cli-service build --mode staging",
    "test": "vue-cli-service test --mode test"
  }
}

3. 多环境高级配置技巧

3.1 环境变量智能提示

在TypeScript项目中,我们可以通过声明文件增强环境变量的类型提示。在src目录下创建env.d.ts:

declare namespace NodeJS {
  interface ProcessEnv {
    readonly NODE_ENV: 'development' | 'production' | 'test'
    readonly VUE_APP_TITLE: string
    readonly VUE_APP_API_BASE: string
    // 添加其他自定义环境变量
  }
}

这样在代码中使用process.env时,就能获得自动补全和类型检查,避免拼写错误。

3.2 动态配置方案

有时候我们需要在运行时动态加载配置,而不是在构建时硬编码。这时可以结合public目录下的配置文件:

  1. 在public目录创建config.json
{
  "apiBaseUrl": "/api",
  "cdnUrl": "/static"
}
  1. 在应用启动时加载配置
// src/utils/config.js
let runtimeConfig = {}

export async function loadConfig() {
  const response = await fetch('/config.json')
  runtimeConfig = await response.json()
}

export function getConfig(key) {
  return runtimeConfig[key] || process.env[`VUE_APP_${key}`]
}

这种方案特别适合需要频繁修改配置的场景,比如不同客户部署的定制化版本。

3.3 安全最佳实践

环境变量中经常会包含敏感信息,这里有几个安全建议:

  1. 永远不要把敏感信息提交到代码仓库
# .gitignore
.env.local
*.env.*.local
  1. 使用加密工具管理敏感变量
# 安装加密工具
npm install --save-dev dotenv-vault

# 加密.env文件
npx dotenv-vault local encrypt
  1. 在CI/CD中安全注入变量
# GitHub Actions示例
jobs:
  build:
    steps:
      - uses: actions/checkout@v2
      - run: npm install
      - run: npm run build
        env:
          VUE_APP_SECRET: ${{ secrets.API_KEY }}

4. 与CI/CD流程集成

4.1 自动化构建配置

现代前端项目通常会通过CI/CD工具实现自动化构建和部署。以GitHub Actions为例:

name: Build and Deploy

on:
  push:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        env: [production, staging]
    
    steps:
      - uses: actions/checkout@v2
      
      - name: Setup Node
        uses: actions/setup-node@v2
        with:
          node-version: '14'
      
      - name: Install dependencies
        run: npm install
      
      - name: Build for ${{ matrix.env }}
        run: npm run build:${{ matrix.env }}
        env:
          VUE_APP_VERSION: ${{ github.sha }}
          VUE_APP_BUILD_TIME: ${{ steps.get-date.outputs.date }}
      
      - name: Upload artifacts
        uses: actions/upload-artifact@v2
        with:
          name: dist-${{ matrix.env }}
          path: dist

4.2 多阶段部署策略

对于企业级项目,建议采用分阶段部署策略:

  1. 开发阶段:每次push触发自动构建和部署到开发环境
  2. 测试阶段:合并到test分支时部署到测试环境
  3. 预发布阶段:打tag时部署到staging环境
  4. 生产阶段:手动触发生产环境部署

对应的GitHub Workflow配置:

jobs:
  deploy:
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      
      - name: Setup Node
        uses: actions/setup-node@v2
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build production
        run: npm run build
        env:
          VUE_APP_API_URL: ${{ secrets.PROD_API_URL }}
      
      - name: Deploy to Production
        uses: JamesIves/github-pages-deploy-action@4.1.5
        with:
          branch: gh-pages
          folder: dist

4.3 环境健康检查

部署完成后,建议添加自动化健康检查:

// src/utils/healthCheck.js
export async function healthCheck() {
  try {
    const response = await fetch(`${process.env.VUE_APP_API_URL}/health`)
    if (!response.ok) throw new Error('Health check failed')
    
    const data = await response.json()
    console.log(`Environment: ${process.env.NODE_ENV}`)
    console.log(`API Version: ${data.version}`)
    return true
  } catch (error) {
    console.error('Health check error:', error)
    return false
  }
}

在main.js中调用:

import { healthCheck } from './utils/healthCheck'

healthCheck().then(healthy => {
  if (!healthy) {
    // 显示环境错误提示
  }
})

5. 常见问题与解决方案

5.1 环境变量未生效排查

当环境变量没有按预期生效时,可以按照以下步骤排查:

  1. 确认文件名是否正确匹配mode参数
  2. 检查变量名是否以VUE_APP_开头
  3. 确保文件位于项目根目录
  4. 重启开发服务器(环境变量在启动时被固化)
  5. 检查变量名拼写是否正确
  6. 查看webpack配置是否有覆盖

可以在vue.config.js中添加调试信息:

console.log('Env variables:', process.env)

5.2 跨项目共享配置

在微前端架构中,多个子项目可能需要共享基础配置。这时可以创建共享配置模块:

  1. 创建config包
packages/
  config/
    src/
      index.js
    package.json
  1. 在index.js中导出通用配置
const sharedConfig = {
  appName: 'My App',
  apiTimeout: 30000
}

module.exports = {
  ...sharedConfig,
  ...process.env
}
  1. 在各子项目中引用
const config = require('@myorg/config')

5.3 本地开发代理设置

开发时经常需要解决跨域问题,可以在vue.config.js中配置代理:

module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: process.env.VUE_APP_PROXY_TARGET || 'http://localhost:3000',
        changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        }
      }
    }
  }
}

对应的.env.development文件:

VUE_APP_PROXY_TARGET=https://dev-api.example.com

6. 现代替代方案探索

6.1 Vite环境变量处理

如果你使用Vite而非webpack,环境变量的处理方式略有不同:

  1. 变量前缀变为VITE_而非VUE_APP_
  2. 暴露方式更加直接,无需额外配置
  3. 支持更灵活的.env文件命名

示例:

# .env
VITE_API_URL=https://api.example.com

# 代码中使用
console.log(import.meta.env.VITE_API_URL)

6.2 动态环境注入

对于需要完全动态配置的场景,可以考虑在Docker容器启动时注入:

FROM nginx:alpine
COPY dist /usr/share/nginx/html
COPY env.sh /docker-entrypoint.d/

RUN chmod +x /docker-entrypoint.d/env.sh

env.sh脚本:

#!/bin/sh

# 生成runtime-config.js
cat > /usr/share/nginx/html/runtime-config.js <<EOF
window.__env__ = {
  API_URL: "${API_URL:-https://api.example.com}",
  DEBUG: "${DEBUG:-false}"
};
EOF

exec "$@"

6.3 功能开关实现

基于环境变量可以实现功能开关:

// src/features.js
export const features = {
  newDashboard: process.env.VUE_APP_FEATURE_DASHBOARD === 'true',
  experimentalAPI: process.env.VUE_APP_FEATURE_EXPERIMENTAL_API === 'true'
}

// 使用方式
import { features } from './features'

if (features.newDashboard) {
  // 启用新功能
}

这样可以通过环境变量控制功能的开启和关闭,而无需修改代码或重新构建。

更多推荐