Appearance
开发环境与生产环境区分
课程概述
本章节深入探讨如何在前端项目中区分开发环境和生产环境,学习针对不同环境的优化策略。正确的环境区分能够提高开发效率,优化生产性能,确保应用安全。
学习目标
- 理解开发环境和生产环境的区别
- 掌握环境检测和区分方法
- 学习针对不同环境的配置策略
- 了解开发和生产的构建差异
- 掌握条件编译和代码分支
- 学习环境特定的优化技巧
第一部分:环境概念
1.1 什么是环境
环境(Environment)是指应用程序运行的上下文,不同环境有不同的配置、行为和优化策略。
常见环境:
javascript
1. Development - 开发环境
2. Staging - 预发布环境
3. Production - 生产环境
4. Test - 测试环境1.2 环境差异对比
| 特性 | 开发环境 | 生产环境 |
|---|---|---|
| 代码压缩 | 否 | 是 |
| Source Map | 详细 | 简化/无 |
| 热更新 | 是 | 否 |
| 调试工具 | 启用 | 禁用 |
| 错误提示 | 详细 | 简化 |
| API 端点 | 本地/测试 | 正式 |
| 日志级别 | Debug | Error |
| 性能监控 | 可选 | 必需 |
| 缓存策略 | 禁用 | 启用 |
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 env2.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'))
: () => null4.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 api5.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 -- production6.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()总结
本章全面介绍了开发环境和生产环境的区分:
- 环境概念 - 理解不同环境的特点和差异
- 环境检测 - 各种构建工具的环境检测方法
- 开发配置 - 开发环境的优化配置
- 生产配置 - 生产环境的优化策略
- 特定功能 - 环境特定的功能实现
- 构建脚本 - 多环境构建和部署
- 最佳实践 - 配置组织和类型安全
正确区分和配置不同环境是构建高质量应用的基础。