编写和测试代码
生产环境构建
自动化测试验证
发布到生产环境
性能监控和优化
// package.json - 优化脚本
{
"name": "my-react-app",
"version": "1.0.0",
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"build:prod": "npm run build && npm run optimize",
"test": "react-scripts test",
"eject": "react-scripts eject",
"optimize": "npm run optimize:images && npm run optimize:bundle",
"optimize:images": "imagemin src/images/* --out-dir=build/images",
"optimize:bundle": "webpack-bundle-analyzer build/stats.json",
"predeploy": "npm run build",
"deploy": "gh-pages -d build"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
// 环境配置文件
// .env.production
REACT_APP_API_URL=https://api.example.com
REACT_APP_SENTRY_DSN=https://xxx@sentry.io/xxx
REACT_APP_GA_TRACKING_ID=UA-XXXXX-Y
REACT_APP_VERSION=$npm_package_version
// .env.development
REACT_APP_API_URL=http://localhost:3001
REACT_APP_DEBUG=true
// 在代码中使用
const apiUrl = process.env.REACT_APP_API_URL;
const isProduction = process.env.NODE_ENV === 'production';
// 条件引入
if (isProduction) {
const Sentry = require('@sentry/react');
Sentry.init({ dsn: process.env.REACT_APP_SENTRY_DSN });
}
# 基础构建命令
npm run build
# 带分析功能的构建
npm run build -- --profile --stats
# 使用环境变量构建
REACT_APP_API_URL=https://api.example.com npm run build
# 检查构建输出
ls -la build/
# 输出:
# asset-manifest.json
# index.html
# robots.txt
# static/
# css/
# js/
# media/
# 本地测试构建结果
npx serve -s build -l 3000
# 或
npm install -g serve
serve -s build
--profile标志生成性能分析数据--stats生成Webpack统计文件用于分析# 安装分析工具
npm install --save-dev source-map-explorer webpack-bundle-analyzer
# package.json中添加脚本
"scripts": {
"analyze": "source-map-explorer build/static/js/*.js",
"analyze:bundle": "webpack-bundle-analyzer build/stats.json"
}
# 生成分析报告
npm run build -- --stats
npm run analyze:bundle
# 分析结果示例
# 📦 总大小: 1.2MB (gzip: 350KB)
# ├── React + ReactDOM: 120KB
# ├── 业务代码: 450KB
# ├── 第三方库: 550KB
# └── 样式: 80KB
1.2s
目标: < 1.8s450KB
目标: < 500KB80KB
目标: < 100KB15
目标: < 20React官方推荐的部署平台,专为前端优化
# 部署命令
npm i -g vercel
vercel
# 或直接推送代码
git push
功能丰富的静态站点部署平台
# 安装Netlify CLI
npm i -g netlify-cli
netlify deploy --prod
# 或连接Git仓库自动部署
企业级可扩展部署方案
# AWS CLI配置
aws s3 sync build/ s3://bucket-name
aws cloudfront create-invalidation \
--distribution-id DISTRIBUTION_ID \
--paths "/*"
跨平台一致的部署环境
# Dockerfile示例
FROM nginx:alpine
COPY build/ /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
完全控制的自托管方案
# Nginx配置示例
server {
listen 80;
server_name example.com;
root /var/www/react-app;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}
自动化测试和部署流程
# GitHub Actions示例
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm ci && npm run build
# Dockerfile - 多阶段构建优化
# 第一阶段:构建阶段
FROM node:16-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# 第二阶段:生产阶段
FROM nginx:alpine
# 复制构建产物
COPY --from=builder /app/build /usr/share/nginx/html
# 复制自定义nginx配置
COPY nginx.conf /etc/nginx/conf.d/default.conf
# 添加健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:80/ || exit 1
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
# docker-compose.yml
version: '3.8'
services:
react-app:
build: .
ports:
- "80:80"
environment:
- NODE_ENV=production
- REACT_APP_API_URL=https://api.example.com
volumes:
- ./logs:/var/log/nginx
restart: unless-stopped
networks:
- webnet
networks:
webnet:
driver: bridge
# nginx.conf
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html index.htm;
# Gzip压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript
application/javascript application/xml+rss;
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# React路由支持
location / {
try_files $uri $uri/ /index.html;
}
# 静态资源缓存
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# 健康检查端点
location /health {
access_log off;
return 200 "healthy\n";
}
}
# .github/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test -- --coverage --watchAll=false
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
build:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
env:
REACT_APP_API_URL: ${{ secrets.REACT_APP_API_URL }}
REACT_APP_VERSION: ${{ github.sha }}
- name: Analyze bundle size
uses: preactjs/compressed-size-action@v2
with:
pattern: "build/static/**/*.{js,css}"
repo-token: ${{ secrets.GITHUB_TOKEN }}
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Deploy to Vercel
uses: amondnet/vercel-action@v20
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.ORG_ID}}
vercel-project-id: ${{ secrets.PROJECT_ID}}
vercel-args: '--prod'
- name: Notify deployment
run: |
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"🚀 Production deployment completed!\nVersion: ${{ github.sha }}\nURL: https://app.example.com"}' \
${{ secrets.SLACK_WEBHOOK_URL }}
# Azure DevOps Pipeline
trigger:
branches:
include:
- main
pool:
vmImage: 'ubuntu-latest'
variables:
npm_config_cache: $(Pipeline.Workspace)/.npm
stages:
- stage: Build
displayName: Build stage
jobs:
- job: Build
displayName: Build
steps:
- task: NodeTool@0
inputs:
versionSpec: '16.x'
- script: |
npm ci
npm run build
displayName: 'npm install and build'
- stage: Deploy
displayName: Deploy stage
dependsOn: Build
jobs:
- deployment: Deploy
displayName: Deploy
environment: 'production'
strategy:
runOnce:
deploy:
steps:
- task: AzureWebApp@1
inputs:
azureSubscription: 'Azure Subscription'
appName: 'my-react-app'
package: '$(System.DefaultWorkingDirectory)/build'
// 环境变量配置最佳实践
// .env.production
REACT_APP_API_URL=https://api.production.com
REACT_APP_SENTRY_DSN=https://xxx@sentry.io/xxx
REACT_APP_GA_TRACKING_ID=UA-XXXXX-Y
REACT_APP_VERSION=$npm_package_version
REACT_APP_BUILD_DATE=$npm_package_build_date
// .env.staging
REACT_APP_API_URL=https://api.staging.com
REACT_APP_SENTRY_DSN=https://xxx@sentry.io/xxx
REACT_APP_ENVIRONMENT=staging
// .env.development
REACT_APP_API_URL=http://localhost:3001
REACT_APP_DEBUG=true
REACT_APP_MOCK_API=true
// 环境配置文件
// src/config/env.js
const env = {
apiUrl: process.env.REACT_APP_API_URL,
environment: process.env.NODE_ENV,
isDevelopment: process.env.NODE_ENV === 'development',
isProduction: process.env.NODE_ENV === 'production',
sentryDsn: process.env.REACT_APP_SENTRY_DSN,
gaTrackingId: process.env.REACT_APP_GA_TRACKING_ID,
version: process.env.REACT_APP_VERSION || '1.0.0',
// 验证必需的环境变量
validate() {
const required = ['REACT_APP_API_URL'];
const missing = required.filter(key => !process.env[key]);
if (missing.length > 0) {
throw new Error(`缺少必需的环境变量: ${missing.join(', ')}`);
}
}
};
// 在应用启动时验证
env.validate();
export default env;
// 使用示例
import env from './config/env';
if (env.isProduction) {
// 初始化生产环境专用的库
Sentry.init({ dsn: env.sentryDsn });
ReactGA.initialize(env.gaTrackingId);
}
// 在组件中使用
function App() {
return (
<div>
<p>API端点: {env.apiUrl}</p>
<p>环境: {env.environment}</p>
<p>版本: {env.version}</p>
</div>
);
}
| 监控指标 | 工具/方法 | 目标值 | 告警阈值 |
|---|---|---|---|
| 页面加载时间 | Google Lighthouse, Web Vitals | < 3秒 | > 5秒 |
| JavaScript错误率 | Sentry, LogRocket | < 0.1% | > 1% |
| API响应时间 | New Relic, Datadog | < 200ms | > 500ms |
| 服务器状态码 | Uptime Robot, Pingdom | 100% 200状态码 | 5xx错误 |
| 用户会话时长 | Google Analytics, Amplitude | > 2分钟 | < 30秒 |
// 性能监控集成示例
// src/monitoring.js
import * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/tracing';
import ReactGA from 'react-ga4';
export const initMonitoring = () => {
// 只在生产环境初始化
if (process.env.NODE_ENV !== 'production') {
return;
}
// 初始化Sentry错误监控
Sentry.init({
dsn: process.env.REACT_APP_SENTRY_DSN,
integrations: [new BrowserTracing()],
tracesSampleRate: 0.2, // 采样率
environment: process.env.REACT_APP_ENVIRONMENT || 'production',
release: process.env.REACT_APP_VERSION,
beforeSend(event) {
// 过滤不需要的错误
if (event.exception?.values?.[0]?.value?.includes('ResizeObserver')) {
return null;
}
return event;
}
});
// 初始化Google Analytics
if (process.env.REACT_APP_GA_TRACKING_ID) {
ReactGA.initialize(process.env.REACT_APP_GA_TRACKING_ID);
}
// 性能监控
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('[Performance]', entry.name, entry.duration);
// 发送到分析服务
if (entry.duration > 100) { // 超过100ms
Sentry.addBreadcrumb({
category: 'performance',
message: `Slow ${entry.name}: ${entry.duration}ms`,
level: 'warning'
});
}
}
});
observer.observe({ entryTypes: ['measure', 'paint'] });
}
// 错误边界集成
const originalErrorHandler = window.onerror;
window.onerror = function(message, source, lineno, colno, error) {
console.error('Global error caught:', { message, source, lineno, colno, error });
// 发送到Sentry
Sentry.captureException(error);
// 调用原始处理器
if (originalErrorHandler) {
originalErrorHandler.apply(this, arguments);
}
};
};
// 在index.js中调用
import { initMonitoring } from './monitoring';
initMonitoring();