Skip to content

开发环境与生产环境区分

课程概述

本章节深入探讨如何在前端项目中区分开发环境和生产环境,学习针对不同环境的优化策略。正确的环境区分能够提高开发效率,优化生产性能,确保应用安全。

学习目标

  • 理解开发环境和生产环境的区别
  • 掌握环境检测和区分方法
  • 学习针对不同环境的配置策略
  • 了解开发和生产的构建差异
  • 掌握条件编译和代码分支
  • 学习环境特定的优化技巧

第一部分:环境概念

1.1 什么是环境

环境(Environment)是指应用程序运行的上下文,不同环境有不同的配置、行为和优化策略。

常见环境:

javascript
1. Development  - 开发环境
2. Staging      - 预发布环境
3. Production   - 生产环境
4. Test         - 测试环境

1.2 环境差异对比

特性开发环境生产环境
代码压缩
Source Map详细简化/无
热更新
调试工具启用禁用
错误提示详细简化
API 端点本地/测试正式
日志级别DebugError
性能监控可选必需
缓存策略禁用启用

1.3 为什么要区分环境

javascript
1. 开发效率 - 开发环境提供更好的调试体验
2. 性能优化 - 生产环境进行代码压缩和优化
3. 安全性 - 生产环境隐藏敏感信息
4. 配置隔离 - 不同环境使用不同的配置
5. 功能控制 - 某些功能仅在特定环境启用

第二部分:环境检测

2.1 Vite 环境检测

typescript
// 使用 import.meta.env
const isDevelopment = import.meta.env.DEV
const isProduction = import.meta.env.PROD
const mode = import.meta.env.MODE

if (isDevelopment) {
  console.log('Running in development mode')
}

if (isProduction) {
  console.log('Running in production mode')
}

详细检测:

typescript
// src/utils/env.ts
export const env = {
  isDev: import.meta.env.DEV,
  isProd: import.meta.env.PROD,
  mode: import.meta.env.MODE,
  baseUrl: import.meta.env.BASE_URL,
  
  isLocal: import.meta.env.MODE === 'development',
  isStaging: import.meta.env.MODE === 'staging',
  isProduction: import.meta.env.MODE === 'production',
}

2.2 Create React App 环境检测

javascript
// 使用 process.env.NODE_ENV
const isDevelopment = process.env.NODE_ENV === 'development'
const isProduction = process.env.NODE_ENV === 'production'
const isTest = process.env.NODE_ENV === 'test'

// 创建环境工具
const env = {
  isDev: isDevelopment,
  isProd: isProduction,
  isTest: isTest,
}

export default env

2.3 Next.js 环境检测

typescript
// Next.js 环境检测
export const env = {
  isDev: process.env.NODE_ENV === 'development',
  isProd: process.env.NODE_ENV === 'production',
  isServer: typeof window === 'undefined',
  isClient: typeof window !== 'undefined',
}

// 使用
if (env.isDev && env.isClient) {
  // 仅在客户端开发环境执行
}

2.4 Webpack 环境检测

javascript
// webpack.config.js
module.exports = (env, argv) => {
  const isDevelopment = argv.mode === 'development'
  const isProduction = argv.mode === 'production'
  
  return {
    mode: argv.mode,
    // ...
  }
}
javascript
// 代码中使用
const isDev = process.env.NODE_ENV === 'development'

第三部分:开发环境配置

3.1 开发服务器配置

Vite 开发配置:

typescript
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'

export default defineConfig(({ mode }) => {
  const isDev = mode === 'development'
  
  return {
    plugins: [react()],
    
    server: isDev ? {
      port: 3000,
      open: true,
      host: '0.0.0.0',
      
      // 热更新
      hmr: {
        overlay: true
      },
      
      // 代理
      proxy: {
        '/api': {
          target: 'http://localhost:8080',
          changeOrigin: true
        }
      }
    } : undefined,
    
    // 开发环境 Source Map
    build: {
      sourcemap: isDev ? 'inline' : false
    }
  }
})

Webpack 开发配置:

javascript
// webpack.dev.js
module.exports = {
  mode: 'development',
  
  devtool: 'eval-source-map',
  
  devServer: {
    port: 3000,
    hot: true,
    open: true,
    historyApiFallback: true,
    
    proxy: {
      '/api': 'http://localhost:8080'
    }
  },
  
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
}

3.2 开发工具配置

typescript
// src/config/dev-tools.ts
export function setupDevTools() {
  if (import.meta.env.DEV) {
    // React DevTools
    if (typeof window !== 'undefined') {
      window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.__REACT_DEVTOOLS_GLOBAL_HOOK__ || {}
    }
    
    // Redux DevTools
    if (typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__) {
      // 启用 Redux DevTools
    }
    
    // 性能监控
    if ('performance' in window) {
      console.log('Performance API enabled')
    }
    
    // 日志增强
    console.log('%c开发模式已启用', 'color: green; font-weight: bold')
  }
}

3.3 调试辅助

typescript
// src/utils/debug.ts
export const debug = {
  log: (...args: any[]) => {
    if (import.meta.env.DEV) {
      console.log('[DEBUG]', ...args)
    }
  },
  
  warn: (...args: any[]) => {
    if (import.meta.env.DEV) {
      console.warn('[WARN]', ...args)
    }
  },
  
  error: (...args: any[]) => {
    if (import.meta.env.DEV) {
      console.error('[ERROR]', ...args)
    }
  },
  
  table: (data: any) => {
    if (import.meta.env.DEV) {
      console.table(data)
    }
  },
  
  time: (label: string) => {
    if (import.meta.env.DEV) {
      console.time(label)
    }
  },
  
  timeEnd: (label: string) => {
    if (import.meta.env.DEV) {
      console.timeEnd(label)
    }
  }
}

使用示例:

typescript
import { debug } from '@/utils/debug'

function fetchData() {
  debug.time('fetchData')
  
  // API 调用
  const data = await api.get('/data')
  
  debug.log('Data fetched:', data)
  debug.table(data)
  debug.timeEnd('fetchData')
  
  return data
}

3.4 错误边界(开发环境)

typescript
// src/components/DevErrorBoundary.tsx
import { Component, ReactNode } from 'react'

interface Props {
  children: ReactNode
}

interface State {
  hasError: boolean
  error?: Error
  errorInfo?: any
}

export class DevErrorBoundary extends Component<Props, State> {
  state: State = { hasError: false }
  
  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error }
  }
  
  componentDidCatch(error: Error, errorInfo: any) {
    this.setState({ error, errorInfo })
    
    if (import.meta.env.DEV) {
      console.error('Error caught by boundary:', error)
      console.error('Component stack:', errorInfo.componentStack)
    }
  }
  
  render() {
    if (this.state.hasError && import.meta.env.DEV) {
      return (
        <div style={{ padding: 20, backgroundColor: '#fee' }}>
          <h2>开发环境错误</h2>
          <details style={{ whiteSpace: 'pre-wrap' }}>
            <summary>错误详情</summary>
            {this.state.error?.toString()}
            <br />
            {this.state.errorInfo?.componentStack}
          </details>
        </div>
      )
    }
    
    return this.props.children
  }
}

第四部分:生产环境配置

4.1 生产构建配置

Vite 生产配置:

typescript
// vite.config.ts
export default defineConfig(({ mode }) => {
  const isProd = mode === 'production'
  
  return {
    build: isProd ? {
      // 输出目录
      outDir: 'dist',
      
      // 资源内联限制
      assetsInlineLimit: 4096,
      
      // CSS 代码分割
      cssCodeSplit: true,
      
      // 源码映射
      sourcemap: false,
      
      // 压缩
      minify: 'terser',
      terserOptions: {
        compress: {
          drop_console: true,
          drop_debugger: true,
          pure_funcs: ['console.log']
        }
      },
      
      // 分包策略
      rollupOptions: {
        output: {
          manualChunks: {
            'react-vendor': ['react', 'react-dom'],
            'router': ['react-router-dom']
          }
        }
      }
    } : undefined
  }
})

Webpack 生产配置:

javascript
// webpack.prod.js
const TerserPlugin = require('terser-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  mode: 'production',
  
  devtool: 'source-map',
  
  output: {
    filename: 'js/[name].[contenthash:8].js',
    chunkFilename: 'js/[name].[contenthash:8].chunk.js',
    clean: true
  },
  
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      }
    ]
  },
  
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/[name].[contenthash:8].css'
    })
  ],
  
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: true,
            drop_debugger: true
          }
        }
      }),
      new CssMinimizerPlugin()
    ],
    
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: 10
        }
      }
    }
  }
}

4.2 代码优化

条件编译:

typescript
// src/utils/logger.ts
class Logger {
  log(...args: any[]) {
    if (import.meta.env.DEV) {
      console.log(...args)
    }
  }
  
  error(...args: any[]) {
    // 生产环境发送到错误监控服务
    if (import.meta.env.PROD) {
      this.sendToErrorTracking(args)
    } else {
      console.error(...args)
    }
  }
  
  private sendToErrorTracking(args: any[]) {
    // Sentry, LogRocket 等
  }
}

export const logger = new Logger()

死代码消除:

typescript
// 开发环境代码会被 tree-shake 掉
if (import.meta.env.DEV) {
  // 这些代码在生产构建中会被完全移除
  import('./dev-tools').then(({ setupDevTools }) => {
    setupDevTools()
  })
}

// 或使用动态导入
const DevPanel = import.meta.env.DEV 
  ? lazy(() => import('./DevPanel'))
  : () => null

4.3 性能监控

typescript
// src/utils/performance.ts
export function setupPerformanceMonitoring() {
  if (import.meta.env.PROD && 'performance' in window) {
    // Web Vitals
    import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
      getCLS(console.log)
      getFID(console.log)
      getFCP(console.log)
      getLCP(console.log)
      getTTFB(console.log)
    })
    
    // 资源加载时间
    window.addEventListener('load', () => {
      const perfData = window.performance.timing
      const pageLoadTime = perfData.loadEventEnd - perfData.navigationStart
      
      // 发送到分析服务
      sendAnalytics('page_load_time', pageLoadTime)
    })
  }
}

4.4 错误处理

typescript
// src/utils/error-handler.ts
export function setupErrorHandling() {
  if (import.meta.env.PROD) {
    // 全局错误捕获
    window.addEventListener('error', (event) => {
      // 发送到错误监控
      reportError({
        message: event.message,
        source: event.filename,
        lineno: event.lineno,
        colno: event.colno,
        error: event.error
      })
    })
    
    // Promise 错误捕获
    window.addEventListener('unhandledrejection', (event) => {
      reportError({
        message: 'Unhandled Promise Rejection',
        reason: event.reason
      })
    })
  }
}

function reportError(error: any) {
  // Sentry
  // Sentry.captureException(error)
  
  // 自定义错误服务
  fetch('/api/errors', {
    method: 'POST',
    body: JSON.stringify(error)
  })
}

第五部分:环境特定功能

5.1 API 端点配置

typescript
// src/config/api.ts
const API_ENDPOINTS = {
  development: 'http://localhost:8080/api',
  staging: 'https://api.staging.example.com',
  production: 'https://api.example.com'
}

export const API_URL = API_ENDPOINTS[import.meta.env.MODE as keyof typeof API_ENDPOINTS] || API_ENDPOINTS.development

// 或使用环境变量
export const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8080/api'

API 客户端配置:

typescript
// src/services/api.ts
import axios from 'axios'
import { API_URL } from '@/config/api'

const api = axios.create({
  baseURL: API_URL,
  timeout: import.meta.env.PROD ? 10000 : 30000,
  
  // 生产环境启用重试
  ...(import.meta.env.PROD && {
    retry: 3,
    retryDelay: 1000
  })
})

// 开发环境日志
if (import.meta.env.DEV) {
  api.interceptors.request.use(config => {
    console.log('API Request:', config)
    return config
  })
  
  api.interceptors.response.use(
    response => {
      console.log('API Response:', response)
      return response
    },
    error => {
      console.error('API Error:', error)
      return Promise.reject(error)
    }
  )
}

export default api

5.2 功能开关

typescript
// src/config/features.ts
export const features = {
  // 开发环境专属功能
  devTools: import.meta.env.DEV,
  mockData: import.meta.env.DEV,
  debugPanel: import.meta.env.DEV,
  
  // 生产环境功能
  analytics: import.meta.env.PROD,
  errorTracking: import.meta.env.PROD,
  performanceMonitoring: import.meta.env.PROD,
  
  // 条件功能
  betaFeatures: import.meta.env.VITE_ENABLE_BETA === 'true',
  experimentalUI: import.meta.env.VITE_EXPERIMENTAL_UI === 'true',
}

使用功能开关:

typescript
import { features } from '@/config/features'

function App() {
  return (
    <div>
      {features.devTools && <DevTools />}
      {features.analytics && <Analytics />}
      {features.betaFeatures && <BetaFeature />}
    </div>
  )
}

5.3 日志系统

typescript
// src/utils/logger.ts
type LogLevel = 'debug' | 'info' | 'warn' | 'error'

class Logger {
  private level: LogLevel = import.meta.env.PROD ? 'error' : 'debug'
  
  private levels: Record<LogLevel, number> = {
    debug: 0,
    info: 1,
    warn: 2,
    error: 3
  }
  
  private shouldLog(level: LogLevel): boolean {
    return this.levels[level] >= this.levels[this.level]
  }
  
  debug(...args: any[]) {
    if (this.shouldLog('debug')) {
      console.log('[DEBUG]', ...args)
    }
  }
  
  info(...args: any[]) {
    if (this.shouldLog('info')) {
      console.info('[INFO]', ...args)
    }
  }
  
  warn(...args: any[]) {
    if (this.shouldLog('warn')) {
      console.warn('[WARN]', ...args)
    }
  }
  
  error(...args: any[]) {
    if (this.shouldLog('error')) {
      console.error('[ERROR]', ...args)
      
      // 生产环境发送到监控
      if (import.meta.env.PROD) {
        this.reportError(args)
      }
    }
  }
  
  private reportError(args: any[]) {
    // 发送到错误监控服务
  }
}

export const logger = new Logger()

5.4 Mock 数据

typescript
// src/mocks/handlers.ts
import { rest } from 'msw'

export const handlers = [
  rest.get('/api/users', (req, res, ctx) => {
    return res(
      ctx.json([
        { id: 1, name: 'John' },
        { id: 2, name: 'Jane' }
      ])
    )
  })
]
typescript
// src/mocks/browser.ts
import { setupWorker } from 'msw'
import { handlers } from './handlers'

export const worker = setupWorker(...handlers)
typescript
// src/main.tsx
async function enableMocking() {
  if (import.meta.env.DEV && import.meta.env.VITE_ENABLE_MOCK === 'true') {
    const { worker } = await import('./mocks/browser')
    return worker.start()
  }
}

enableMocking().then(() => {
  ReactDOM.createRoot(document.getElementById('root')!).render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  )
})

第六部分:构建脚本

6.1 package.json 脚本

json
{
  "scripts": {
    "dev": "vite",
    "dev:staging": "vite --mode staging",
    
    "build": "tsc && vite build",
    "build:dev": "vite build --mode development",
    "build:staging": "vite build --mode staging",
    "build:prod": "vite build --mode production",
    
    "preview": "vite preview",
    "preview:prod": "vite preview --mode production",
    
    "analyze": "vite-bundle-visualizer",
    "type-check": "tsc --noEmit"
  }
}

6.2 环境特定脚本

typescript
// scripts/build.ts
import { build } from 'vite'
import { resolve } from 'path'

const mode = process.argv[2] || 'production'

async function buildApp() {
  console.log(`Building for ${mode}...`)
  
  await build({
    mode,
    root: resolve(__dirname, '..'),
    build: {
      outDir: `dist-${mode}`,
      emptyOutDir: true
    }
  })
  
  console.log(`Build complete for ${mode}`)
}

buildApp().catch(console.error)
bash
# 使用
npm run build -- staging
npm run build -- production

6.3 CI/CD 集成

GitHub Actions:

yaml
# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main, staging, develop]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '20'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build for staging
        if: github.ref == 'refs/heads/staging'
        run: npm run build:staging
        env:
          VITE_API_URL: ${{ secrets.STAGING_API_URL }}
      
      - name: Build for production
        if: github.ref == 'refs/heads/main'
        run: npm run build:prod
        env:
          VITE_API_URL: ${{ secrets.PROD_API_URL }}
      
      - name: Deploy
        run: npm run deploy

第七部分:最佳实践

7.1 环境配置文件组织

config/
├── index.ts              # 统一导出
├── constants.ts          # 常量
├── env.ts               # 环境检测
├── api.ts               # API 配置
├── features.ts          # 功能开关
└── environments/
    ├── development.ts
    ├── staging.ts
    └── production.ts

统一配置管理:

typescript
// config/index.ts
import { development } from './environments/development'
import { staging } from './environments/staging'
import { production } from './environments/production'

const configs = {
  development,
  staging,
  production
}

export const config = configs[import.meta.env.MODE as keyof typeof configs] || development

export * from './constants'
export * from './env'
export * from './features'

7.2 类型安全

typescript
// config/types.ts
export interface AppConfig {
  api: {
    baseUrl: string
    timeout: number
  }
  features: {
    analytics: boolean
    errorTracking: boolean
  }
  external: {
    stripeKey: string
    gaId?: string
  }
}

// config/environments/production.ts
import { AppConfig } from '../types'

export const production: AppConfig = {
  api: {
    baseUrl: 'https://api.example.com',
    timeout: 10000
  },
  features: {
    analytics: true,
    errorTracking: true
  },
  external: {
    stripeKey: import.meta.env.VITE_STRIPE_KEY,
    gaId: import.meta.env.VITE_GA_ID
  }
}

7.3 环境验证

typescript
// config/validation.ts
import { z } from 'zod'

const configSchema = z.object({
  api: z.object({
    baseUrl: z.string().url(),
    timeout: z.number().positive()
  }),
  features: z.object({
    analytics: z.boolean(),
    errorTracking: z.boolean()
  }),
  external: z.object({
    stripeKey: z.string().min(1),
    gaId: z.string().optional()
  })
})

export function validateConfig(config: unknown) {
  return configSchema.parse(config)
}

7.4 完整示例

typescript
// src/config/index.ts
import { z } from 'zod'

// 环境检测
export const env = {
  isDev: import.meta.env.DEV,
  isProd: import.meta.env.PROD,
  mode: import.meta.env.MODE,
}

// 配置 Schema
const configSchema = z.object({
  api: z.object({
    baseUrl: z.string().url(),
    timeout: z.number(),
  }),
  features: z.object({
    analytics: z.boolean(),
    devTools: z.boolean(),
  }),
})

// 环境配置
const configs = {
  development: {
    api: {
      baseUrl: 'http://localhost:8080',
      timeout: 30000,
    },
    features: {
      analytics: false,
      devTools: true,
    },
  },
  production: {
    api: {
      baseUrl: 'https://api.example.com',
      timeout: 10000,
    },
    features: {
      analytics: true,
      devTools: false,
    },
  },
}

// 获取并验证配置
function getConfig() {
  const rawConfig = configs[env.mode as keyof typeof configs] || configs.development
  return configSchema.parse(rawConfig)
}

export const config = getConfig()

总结

本章全面介绍了开发环境和生产环境的区分:

  1. 环境概念 - 理解不同环境的特点和差异
  2. 环境检测 - 各种构建工具的环境检测方法
  3. 开发配置 - 开发环境的优化配置
  4. 生产配置 - 生产环境的优化策略
  5. 特定功能 - 环境特定的功能实现
  6. 构建脚本 - 多环境构建和部署
  7. 最佳实践 - 配置组织和类型安全

正确区分和配置不同环境是构建高质量应用的基础。

扩展阅读