Skip to content

React 项目构建与部署

概述

本章将学习如何构建和部署 React 应用,包括生产环境优化、构建配置、部署策略以及性能监控等内容。

🔧 项目构建

生产构建

bash
# Create React App 构建命令
npm run build

# 构建输出分析
npm install -g serve
serve -s build

# 构建分析
npm install --save-dev webpack-bundle-analyzer
npm run build -- --analyze

环境变量配置

bash
# .env 文件
REACT_APP_API_URL=http://localhost:3001
REACT_APP_VERSION=1.0.0

# .env.production
REACT_APP_API_URL=https://api.production.com
REACT_APP_VERSION=1.0.0
jsx
// 环境变量使用
function EnvironmentConfig() {
  const config = {
    apiUrl: process.env.REACT_APP_API_URL,
    version: process.env.REACT_APP_VERSION,
    isDevelopment: process.env.NODE_ENV === 'development',
    isProduction: process.env.NODE_ENV === 'production'
  };
  
  return (
    <div style={{ padding: '20px' }}>
      <h2>环境配置</h2>
      <pre>{JSON.stringify(config, null, 2)}</pre>
    </div>
  );
}

⚡ 性能优化

代码分割

jsx
import { Suspense, lazy } from 'react';

// 懒加载组件
const LazyComponent = lazy(() => import('./LazyComponent'));

function CodeSplittingExample() {
  const [showLazy, setShowLazy] = React.useState(false);
  
  return (
    <div>
      <h2>代码分割演示</h2>
      <button onClick={() => setShowLazy(!showLazy)}>
        {showLazy ? '隐藏' : '显示'}懒加载组件
      </button>
      
      {showLazy && (
        <Suspense fallback={<div>加载中...</div>}>
          <LazyComponent />
        </Suspense>
      )}
    </div>
  );
}

资源优化

jsx
// 图片懒加载
function LazyImage({ src, alt, ...props }) {
  const [isLoaded, setIsLoaded] = React.useState(false);
  const [isInView, setIsInView] = React.useState(false);
  const imgRef = React.useRef();
  
  React.useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsInView(true);
          observer.disconnect();
        }
      },
      { threshold: 0.1 }
    );
    
    if (imgRef.current) {
      observer.observe(imgRef.current);
    }
    
    return () => observer.disconnect();
  }, []);
  
  return (
    <div ref={imgRef} style={{ minHeight: '200px', backgroundColor: '#f8f9fa' }}>
      {isInView && (
        <img
          src={src}
          alt={alt}
          onLoad={() => setIsLoaded(true)}
          style={{
            opacity: isLoaded ? 1 : 0,
            transition: 'opacity 0.3s',
            ...props.style
          }}
          {...props}
        />
      )}
    </div>
  );
}

🚀 部署策略

静态文件部署

json
{
  "scripts": {
    "build": "react-scripts build",
    "deploy": "npm run build && cp -r build/* /var/www/html/",
    "deploy:s3": "aws s3 sync build/ s3://my-bucket --delete",
    "deploy:gh-pages": "gh-pages -d build"
  }
}

Nginx 配置

nginx
server {
    listen 80;
    server_name example.com;
    root /var/www/html;
    index index.html;
    
    # 处理客户端路由
    location / {
        try_files $uri $uri/ /index.html;
    }
    
    # 静态资源缓存
    location /static/ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
    
    # API 代理
    location /api/ {
        proxy_pass http://localhost:3001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Docker 部署

dockerfile
# Dockerfile
FROM node:18-alpine as build

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
yaml
# docker-compose.yml
version: '3.8'
services:
  app:
    build: .
    ports:
      - "80:80"
    environment:
      - NODE_ENV=production
    restart: unless-stopped

CI/CD 配置

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

on:
  push:
    branches: [main]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v2
    
    - name: Setup Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run tests
      run: npm test -- --coverage --watchAll=false
    
    - name: Build application
      run: npm run build
      env:
        REACT_APP_API_URL: ${{ secrets.API_URL }}
    
    - name: Deploy to S3
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: us-east-1
    
    - name: Sync to S3
      run: aws s3 sync build/ s3://my-bucket --delete
    
    - name: Invalidate CloudFront
      run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_ID }} --paths "/*"

📊 监控和分析

性能监控

jsx
// Web Vitals 监控
function performanceMonitoring() {
  // 模拟 web-vitals 库
  const getCLS = (onPerfEntry) => {
    // Cumulative Layout Shift
    if (onPerfEntry && onPerfEntry instanceof Function) {
      onPerfEntry({ name: 'CLS', value: 0.1 });
    }
  };
  
  const getFID = (onPerfEntry) => {
    // First Input Delay
    if (onPerfEntry && onPerfEntry instanceof Function) {
      onPerfEntry({ name: 'FID', value: 100 });
    }
  };
  
  const getFCP = (onPerfEntry) => {
    // First Contentful Paint
    if (onPerfEntry && onPerfEntry instanceof Function) {
      onPerfEntry({ name: 'FCP', value: 1500 });
    }
  };
  
  const getLCP = (onPerfEntry) => {
    // Largest Contentful Paint
    if (onPerfEntry && onPerfEntry instanceof Function) {
      onPerfEntry({ name: 'LCP', value: 2500 });
    }
  };
  
  const getTTFB = (onPerfEntry) => {
    // Time to First Byte
    if (onPerfEntry && onPerfEntry instanceof Function) {
      onPerfEntry({ name: 'TTFB', value: 200 });
    }
  };
  
  return { getCLS, getFID, getFCP, getLCP, getTTFB };
}

// 错误边界和错误报告
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }
  
  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }
  
  componentDidCatch(error, errorInfo) {
    // 发送错误到监控服务
    this.reportError(error, errorInfo);
  }
  
  reportError = (error, errorInfo) => {
    // 模拟错误报告
    const errorReport = {
      message: error.message,
      stack: error.stack,
      componentStack: errorInfo.componentStack,
      timestamp: new Date().toISOString(),
      userAgent: navigator.userAgent,
      url: window.location.href
    };
    
    console.error('Error reported:', errorReport);
    
    // 实际项目中发送到错误监控服务
    // fetch('/api/errors', {
    //   method: 'POST',
    //   body: JSON.stringify(errorReport)
    // });
  };
  
  render() {
    if (this.state.hasError) {
      return (
        <div style={{ padding: '20px', textAlign: 'center' }}>
          <h2>出现错误</h2>
          <p>应用遇到了一个错误,请刷新页面重试。</p>
          <button onClick={() => window.location.reload()}>
            刷新页面
          </button>
        </div>
      );
    }
    
    return this.props.children;
  }
}

分析工具集成

jsx
// Google Analytics 集成
function useAnalytics() {
  React.useEffect(() => {
    // 模拟 GA 初始化
    if (process.env.NODE_ENV === 'production') {
      console.log('Google Analytics initialized');
    }
  }, []);
  
  const trackEvent = (action, category, label, value) => {
    if (process.env.NODE_ENV === 'production') {
      // gtag('event', action, {
      //   event_category: category,
      //   event_label: label,
      //   value: value
      // });
      console.log('Event tracked:', { action, category, label, value });
    }
  };
  
  const trackPageView = (path) => {
    if (process.env.NODE_ENV === 'production') {
      // gtag('config', 'GA_MEASUREMENT_ID', {
      //   page_path: path
      // });
      console.log('Page view tracked:', path);
    }
  };
  
  return { trackEvent, trackPageView };
}

// 使用分析
function AnalyticsExample() {
  const { trackEvent, trackPageView } = useAnalytics();
  
  React.useEffect(() => {
    trackPageView('/analytics-example');
  }, [trackPageView]);
  
  const handleButtonClick = () => {
    trackEvent('click', 'button', 'analytics-example', 1);
  };
  
  return (
    <div style={{ padding: '20px' }}>
      <h2>分析集成示例</h2>
      <button onClick={handleButtonClick}>
        追踪点击事件
      </button>
    </div>
  );
}

🔐 安全最佳实践

安全配置

jsx
// 内容安全策略
function SecurityHeaders() {
  React.useEffect(() => {
    // 在生产环境中,这些应该在服务器级别设置
    const securityHeaders = {
      'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline'",
      'X-Frame-Options': 'DENY',
      'X-Content-Type-Options': 'nosniff',
      'Referrer-Policy': 'strict-origin-when-cross-origin'
    };
    
    console.log('Security headers should be set:', securityHeaders);
  }, []);
  
  return null;
}

// 敏感信息处理
function SecureDataHandling() {
  const [sensitiveData, setSensitiveData] = React.useState(null);
  
  const fetchSecureData = async () => {
    try {
      // 安全的 API 调用
      const token = localStorage.getItem('authToken');
      const response = await fetch('/api/secure-data', {
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json'
        }
      });
      
      if (!response.ok) {
        throw new Error('Unauthorized');
      }
      
      const data = await response.json();
      setSensitiveData(data);
    } catch (error) {
      console.error('Security error:', error);
      // 处理安全错误
    }
  };
  
  return (
    <div style={{ padding: '20px' }}>
      <h2>安全数据处理</h2>
      <button onClick={fetchSecureData}>
        获取安全数据
      </button>
      {sensitiveData && (
        <div>数据已安全获取</div>
      )}
    </div>
  );
}

📝 本章小结

通过本章学习,你应该掌握了:

构建和部署

  • ✅ 生产环境构建优化
  • ✅ 环境变量配置
  • ✅ 静态文件部署
  • ✅ Docker 容器化部署

性能和监控

  • ✅ 代码分割和懒加载
  • ✅ 性能指标监控
  • ✅ 错误追踪和报告
  • ✅ 用户行为分析

最佳实践

  1. 构建优化:启用压缩、代码分割
  2. 缓存策略:合理设置静态资源缓存
  3. 监控告警:实时监控应用性能
  4. 安全防护:设置安全头、数据加密
  5. 持续部署:自动化 CI/CD 流程

继续学习下一章 - React 框架与原生网页的区别

本站内容仅供学习和研究使用。