部署指南 
本指南介绍如何将 Trae 应用程序部署到各种平台和环境,包括云服务、容器化部署和传统服务器部署。
概述 
Trae 支持多种部署方式,从简单的静态网站托管到复杂的容器化微服务架构。选择合适的部署策略取决于您的应用需求、团队规模和预算考虑。
构建准备 
生产构建 
在部署之前,确保您的应用已经过生产优化:
bash
# 安装依赖
npm ci --production
# 运行测试
npm test
# 构建生产版本
npm run build
# 预览构建结果
npm run preview构建优化 
javascript
// trae.config.js - 生产构建配置
export default {
  build: {
    // 输出目录
    outDir: 'dist',
    
    // 启用压缩
    minify: 'terser',
    
    // 禁用 source map(可选)
    sourcemap: false,
    
    // 代码分割
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          router: ['react-router-dom'],
          ui: ['antd']
        }
      }
    },
    
    // Terser 配置
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    }
  },
  
  // 环境变量
  define: {
    __BUILD_TIME__: JSON.stringify(new Date().toISOString()),
    __VERSION__: JSON.stringify(process.env.npm_package_version)
  }
};环境配置 
bash
# .env.production
VITE_API_URL=https://api.myapp.com
VITE_CDN_URL=https://cdn.myapp.com
VITE_SENTRY_DSN=https://your-sentry-dsn
VITE_ANALYTICS_ID=GA_MEASUREMENT_ID静态网站部署 
Vercel 部署 
- 安装 Vercel CLI bash- npm i -g vercel
- 配置 vercel.json json- { "version": 2, "builds": [ { "src": "package.json", "use": "@vercel/static-build", "config": { "distDir": "dist" } } ], "routes": [ { "src": "/api/(.*)", "dest": "/api/$1" }, { "src": "/(.*)", "dest": "/index.html" } ], "headers": [ { "source": "/static/(.*)", "headers": [ { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" } ] } ], "env": { "VITE_API_URL": "@api_url", "VITE_SENTRY_DSN": "@sentry_dsn" } }
- 部署命令 bash- # 部署到预览环境 vercel # 部署到生产环境 vercel --prod
Netlify 部署 
- 配置 netlify.toml toml- [build] publish = "dist" command = "npm run build" [build.environment] NODE_VERSION = "18" [[redirects]] from = "/api/*" to = "https://api.myapp.com/:splat" status = 200 force = true [[redirects]] from = "/*" to = "/index.html" status = 200 [[headers]] for = "/static/*" [headers.values] Cache-Control = "public, max-age=31536000, immutable" [[headers]] for = "/*.html" [headers.values] Cache-Control = "no-cache"
- 环境变量设置 bash- # 通过 Netlify CLI 设置 netlify env:set VITE_API_URL https://api.myapp.com netlify env:set VITE_SENTRY_DSN https://your-sentry-dsn
GitHub Pages 部署 
- GitHub Actions 工作流 yaml- # .github/workflows/deploy.yml name: Deploy to GitHub Pages on: push: branches: [main] workflow_dispatch: permissions: contents: read pages: write id-token: write concurrency: group: "pages" cancel-in-progress: false jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '18' cache: 'npm' - name: Install dependencies run: npm ci - name: Build run: npm run build env: VITE_API_URL: ${{ secrets.API_URL }} VITE_SENTRY_DSN: ${{ secrets.SENTRY_DSN }} - name: Setup Pages uses: actions/configure-pages@v4 - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: path: './dist' deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4
- 配置 base 路径 javascript- // trae.config.js export default { base: process.env.NODE_ENV === 'production' ? '/repository-name/' : '/' };
容器化部署 
Docker 部署 
- 多阶段 Dockerfile dockerfile- # 构建阶段 FROM node:18-alpine as builder WORKDIR /app # 复制 package 文件 COPY package*.json ./ # 安装依赖 RUN npm ci --only=production && npm cache clean --force # 复制源代码 COPY . . # 构建应用 RUN npm run build # 生产阶段 FROM nginx:alpine # 安装 dumb-init RUN apk add --no-cache dumb-init # 创建非 root 用户 RUN addgroup -g 1001 -S nodejs RUN adduser -S nextjs -u 1001 # 复制构建结果 COPY --from=builder /app/dist /usr/share/nginx/html # 复制 nginx 配置 COPY nginx.conf /etc/nginx/nginx.conf # 设置权限 RUN chown -R nextjs:nodejs /usr/share/nginx/html RUN chown -R nextjs:nodejs /var/cache/nginx RUN chown -R nextjs:nodejs /var/log/nginx RUN chown -R nextjs:nodejs /etc/nginx/conf.d RUN touch /var/run/nginx.pid RUN chown -R nextjs:nodejs /var/run/nginx.pid USER nextjs EXPOSE 8080 ENTRYPOINT ["dumb-init", "--"] CMD ["nginx", "-g", "daemon off;"]
- Nginx 配置 nginx- # nginx.conf worker_processes auto; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; # Gzip 压缩 gzip on; gzip_vary on; gzip_min_length 1024; gzip_proxied any; gzip_comp_level 6; gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/atom+xml image/svg+xml; server { listen 8080; server_name localhost; root /usr/share/nginx/html; index index.html; # 安全头 add_header X-Frame-Options "SAMEORIGIN" always; add_header X-XSS-Protection "1; mode=block" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "no-referrer-when-downgrade" always; add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always; # 静态资源缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; access_log off; } # HTML 文件不缓存 location ~* \.html$ { expires -1; add_header Cache-Control "no-cache, no-store, must-revalidate"; } # SPA 路由支持 location / { try_files $uri $uri/ /index.html; } # 健康检查 location /health { access_log off; return 200 "healthy\n"; add_header Content-Type text/plain; } } }
- Docker Compose yaml- # docker-compose.yml version: '3.8' services: app: build: context: . dockerfile: Dockerfile ports: - "3000:8080" environment: - NODE_ENV=production restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] interval: 30s timeout: 10s retries: 3 labels: - "traefik.enable=true" - "traefik.http.routers.app.rule=Host(`myapp.com`)" - "traefik.http.routers.app.tls=true" - "traefik.http.routers.app.tls.certresolver=letsencrypt" traefik: image: traefik:v2.10 command: - "--api.insecure=true" - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true" - "--certificatesresolvers.letsencrypt.acme.email=admin@myapp.com" - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" ports: - "80:80" - "443:443" - "8080:8080" volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro" - "./letsencrypt:/letsencrypt"
Kubernetes 部署 
- Deployment 配置 yaml- # k8s/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: trae-app labels: app: trae-app spec: replicas: 3 selector: matchLabels: app: trae-app template: metadata: labels: app: trae-app spec: containers: - name: trae-app image: myregistry/trae-app:latest ports: - containerPort: 8080 env: - name: NODE_ENV value: "production" - name: API_URL valueFrom: configMapKeyRef: name: app-config key: api-url resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "256Mi" cpu: "200m" livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 5 periodSeconds: 5
- Service 配置 yaml- # k8s/service.yaml apiVersion: v1 kind: Service metadata: name: trae-app-service spec: selector: app: trae-app ports: - protocol: TCP port: 80 targetPort: 8080 type: ClusterIP
- Ingress 配置 yaml- # k8s/ingress.yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: trae-app-ingress annotations: kubernetes.io/ingress.class: "nginx" cert-manager.io/cluster-issuer: "letsencrypt-prod" nginx.ingress.kubernetes.io/rewrite-target: / spec: tls: - hosts: - myapp.com secretName: trae-app-tls rules: - host: myapp.com http: paths: - path: / pathType: Prefix backend: service: name: trae-app-service port: number: 80
- ConfigMap 配置 yaml- # k8s/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: app-config data: api-url: "https://api.myapp.com" sentry-dsn: "https://your-sentry-dsn"
云平台部署 
AWS 部署 
- S3 + CloudFront 部署 bash- # 安装 AWS CLI npm install -g @aws-amplify/cli # 配置 AWS 凭证 aws configure # 创建 S3 存储桶 aws s3 mb s3://my-trae-app # 上传构建文件 aws s3 sync dist/ s3://my-trae-app --delete # 设置网站配置 aws s3 website s3://my-trae-app --index-document index.html --error-document index.html
- CloudFormation 模板 yaml- # cloudformation.yaml AWSTemplateFormatVersion: '2010-09-09' Description: 'Trae App Infrastructure' Resources: S3Bucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub '${AWS::StackName}-app' WebsiteConfiguration: IndexDocument: index.html ErrorDocument: index.html PublicAccessBlockConfiguration: BlockPublicAcls: false BlockPublicPolicy: false IgnorePublicAcls: false RestrictPublicBuckets: false CloudFrontDistribution: Type: AWS::CloudFront::Distribution Properties: DistributionConfig: Origins: - DomainName: !GetAtt S3Bucket.RegionalDomainName Id: S3Origin S3OriginConfig: OriginAccessIdentity: '' Enabled: true DefaultRootObject: index.html DefaultCacheBehavior: TargetOriginId: S3Origin ViewerProtocolPolicy: redirect-to-https CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad CustomErrorResponses: - ErrorCode: 404 ResponseCode: 200 ResponsePagePath: /index.html - ErrorCode: 403 ResponseCode: 200 ResponsePagePath: /index.html
Google Cloud Platform 部署 
- App Engine 部署 yaml- # app.yaml runtime: nodejs18 handlers: - url: /static static_dir: dist/static secure: always - url: /(.*\.(js|css|ico|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot))$ static_files: dist/\1 upload: dist/(.*\.(js|css|ico|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot))$ secure: always - url: /.* static_files: dist/index.html upload: dist/index.html secure: always env_variables: NODE_ENV: production API_URL: https://api.myapp.com
- Cloud Run 部署 bash- # 构建并推送镜像 gcloud builds submit --tag gcr.io/PROJECT_ID/trae-app # 部署到 Cloud Run gcloud run deploy trae-app \ --image gcr.io/PROJECT_ID/trae-app \ --platform managed \ --region us-central1 \ --allow-unauthenticated
Azure 部署 
- Static Web Apps 部署yaml# .github/workflows/azure-static-web-apps.yml name: Azure Static Web Apps CI/CD on: push: branches: - main pull_request: types: [opened, synchronize, reopened, closed] branches: - main jobs: build_and_deploy_job: if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') runs-on: ubuntu-latest name: Build and Deploy Job steps: - uses: actions/checkout@v3 with: submodules: true - name: Build And Deploy id: builddeploy uses: Azure/static-web-apps-deploy@v1 with: azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }} repo_token: ${{ secrets.GITHUB_TOKEN }} action: "upload" app_location: "/" api_location: "" output_location: "dist"
持续集成/持续部署 (CI/CD) 
GitHub Actions 
yaml
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
env:
  NODE_VERSION: '18'
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run linting
        run: npm run lint
      
      - name: Run tests
        run: npm test -- --coverage
      
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage/lcov.info
  build:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build application
        run: npm run build
        env:
          VITE_API_URL: ${{ secrets.PROD_API_URL }}
          VITE_SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
      
      - name: Upload build artifacts
        uses: actions/upload-artifact@v3
        with:
          name: dist
          path: dist/
  docker:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      
      - name: Download build artifacts
        uses: actions/download-artifact@v3
        with:
          name: dist
          path: dist/
      
      - name: Log in to Container Registry
        uses: docker/login-action@v2
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      
      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v4
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=sha,prefix={{branch}}-
            type=raw,value=latest,enable={{is_default_branch}}
      
      - name: Build and push Docker image
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
  deploy:
    needs: docker
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    environment: production
    steps:
      - name: Deploy to production
        run: |
          echo "Deploying to production..."
          # 这里添加您的部署脚本GitLab CI/CD 
yaml
# .gitlab-ci.yml
stages:
  - test
  - build
  - deploy
variables:
  NODE_VERSION: "18"
  DOCKER_DRIVER: overlay2
  DOCKER_TLS_CERTDIR: "/certs"
cache:
  paths:
    - node_modules/
test:
  stage: test
  image: node:$NODE_VERSION
  script:
    - npm ci
    - npm run lint
    - npm test -- --coverage
  coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
build:
  stage: build
  image: node:$NODE_VERSION
  script:
    - npm ci
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 hour
  only:
    - main
docker:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:latest
    - docker push $CI_REGISTRY_IMAGE:latest
  only:
    - main
deploy_staging:
  stage: deploy
  script:
    - echo "Deploying to staging..."
    # 添加部署到测试环境的脚本
  environment:
    name: staging
    url: https://staging.myapp.com
  only:
    - develop
deploy_production:
  stage: deploy
  script:
    - echo "Deploying to production..."
    # 添加部署到生产环境的脚本
  environment:
    name: production
    url: https://myapp.com
  when: manual
  only:
    - main监控和日志 
应用监控 
javascript
// src/monitoring/sentry.js
import * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/tracing';
Sentry.init({
  dsn: import.meta.env.VITE_SENTRY_DSN,
  environment: import.meta.env.MODE,
  integrations: [
    new BrowserTracing({
      tracingOrigins: ['localhost', /^\//],
    }),
  ],
  tracesSampleRate: 1.0,
  beforeSend(event) {
    // 过滤敏感信息
    if (event.exception) {
      const error = event.exception.values[0];
      if (error.value && error.value.includes('password')) {
        return null;
      }
    }
    return event;
  },
});
export { Sentry };性能监控 
javascript
// src/monitoring/performance.js
export class PerformanceMonitor {
  static init() {
    // Web Vitals 监控
    import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
      getCLS(this.sendToAnalytics);
      getFID(this.sendToAnalytics);
      getFCP(this.sendToAnalytics);
      getLCP(this.sendToAnalytics);
      getTTFB(this.sendToAnalytics);
    });
    
    // 自定义性能指标
    this.measureRouteChanges();
    this.measureAPIRequests();
  }
  
  static sendToAnalytics(metric) {
    // 发送到 Google Analytics
    if (window.gtag) {
      window.gtag('event', metric.name, {
        event_category: 'Web Vitals',
        value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
        event_label: metric.id,
        non_interaction: true,
      });
    }
    
    // 发送到自定义分析服务
    fetch('/api/analytics/performance', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(metric),
    }).catch(console.error);
  }
  
  static measureRouteChanges() {
    let startTime = performance.now();
    
    // 监听路由变化
    window.addEventListener('popstate', () => {
      const duration = performance.now() - startTime;
      this.sendToAnalytics({
        name: 'route_change',
        value: duration,
        id: `route-${Date.now()}`,
      });
      startTime = performance.now();
    });
  }
  
  static measureAPIRequests() {
    const originalFetch = window.fetch;
    
    window.fetch = async (...args) => {
      const startTime = performance.now();
      const response = await originalFetch(...args);
      const duration = performance.now() - startTime;
      
      this.sendToAnalytics({
        name: 'api_request',
        value: duration,
        id: `api-${Date.now()}`,
        url: args[0],
        status: response.status,
      });
      
      return response;
    };
  }
}安全考虑 
内容安全策略 (CSP) 
html
<!-- index.html -->
<meta http-equiv="Content-Security-Policy" content="
  default-src 'self';
  script-src 'self' 'unsafe-inline' https://www.googletagmanager.com;
  style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
  font-src 'self' https://fonts.gstatic.com;
  img-src 'self' data: https:;
  connect-src 'self' https://api.myapp.com https://sentry.io;
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self';
">环境变量安全 
javascript
// scripts/deploy-secrets.js
import { SecretsManager } from 'aws-sdk';
const secretsManager = new SecretsManager({ region: 'us-east-1' });
async function deploySecrets() {
  const secrets = {
    API_KEY: process.env.API_KEY,
    DATABASE_URL: process.env.DATABASE_URL,
    SENTRY_DSN: process.env.SENTRY_DSN,
  };
  
  await secretsManager.createSecret({
    Name: 'trae-app-secrets',
    SecretString: JSON.stringify(secrets),
  }).promise();
  
  console.log('✅ Secrets deployed successfully');
}
deploySecrets().catch(console.error);故障排除 
常见部署问题 
- 路由问题 nginx- # 确保 SPA 路由正确配置 location / { try_files $uri $uri/ /index.html; }
- 环境变量未生效 bash- # 检查环境变量 echo $VITE_API_URL # 重新构建 npm run build
- CORS 问题 javascript- // 配置代理或后端 CORS server: { proxy: { '/api': 'http://localhost:8080' } }
部署检查清单 
- [ ] 环境变量配置正确
- [ ] 构建成功且无错误
- [ ] 静态资源路径正确
- [ ] 路由配置支持 SPA
- [ ] HTTPS 证书配置
- [ ] 缓存策略设置
- [ ] 监控和日志配置
- [ ] 安全头设置
- [ ] 性能优化启用
- [ ] 备份和恢复策略
通过遵循这些部署指南,您可以确保 Trae 应用程序在各种环境中稳定、安全、高效地运行。