Skip to content

最佳实践指南

本指南提供了使用 Trae 开发应用程序的最佳实践,帮助您编写高质量、可维护的代码。

概述

遵循最佳实践可以提高代码质量、团队协作效率,并减少潜在的问题。本指南涵盖了代码组织、性能优化、安全性、测试策略等方面的最佳实践。

项目结构

推荐的目录结构

project-root/
├── src/
│   ├── components/          # 可复用组件
│   │   ├── common/         # 通用组件
│   │   ├── forms/          # 表单组件
│   │   └── layout/         # 布局组件
│   ├── pages/              # 页面组件
│   ├── hooks/              # 自定义 Hooks
│   ├── services/           # API 服务
│   ├── utils/              # 工具函数
│   ├── constants/          # 常量定义
│   ├── types/              # TypeScript 类型定义
│   ├── styles/             # 样式文件
│   └── assets/             # 静态资源
├── tests/                  # 测试文件
│   ├── unit/              # 单元测试
│   ├── integration/       # 集成测试
│   └── e2e/               # 端到端测试
├── docs/                   # 文档
├── scripts/                # 构建和部署脚本
├── .trae/                  # Trae 配置
│   ├── config.json        # 项目配置
│   ├── workflows/         # 工作流定义
│   └── templates/         # 代码模板
└── public/                 # 公共资源

文件命名约定

javascript
// 组件文件 - PascalCase
UserProfile.jsx
NavigationBar.jsx
DataTable.jsx

// 工具函数 - camelCase
formatDate.js
validateEmail.js
api.js

// 常量文件 - UPPER_SNAKE_CASE
API_ENDPOINTS.js
ERROR_MESSAGES.js
CONFIG.js

// 样式文件 - kebab-case
user-profile.css
navigation-bar.scss
data-table.module.css

// 测试文件 - 与源文件同名 + .test/.spec
UserProfile.test.jsx
formatDate.spec.js
api.integration.test.js

代码质量

代码风格和格式化

javascript
// .eslintrc.js
module.exports = {
  extends: [
    'eslint:recommended',
    '@typescript-eslint/recommended',
    'react-hooks/recommended',
    'prettier'
  ],
  plugins: [
    '@typescript-eslint',
    'react-hooks',
    'import'
  ],
  rules: {
    // 强制使用 const/let 而不是 var
    'no-var': 'error',
    'prefer-const': 'error',
    
    // 强制使用严格相等
    'eqeqeq': ['error', 'always'],
    
    // 禁止未使用的变量
    '@typescript-eslint/no-unused-vars': 'error',
    
    // 强制一致的导入顺序
    'import/order': [
      'error',
      {
        'groups': [
          'builtin',
          'external',
          'internal',
          'parent',
          'sibling',
          'index'
        ],
        'newlines-between': 'always',
        'alphabetize': {
          'order': 'asc',
          'caseInsensitive': true
        }
      }
    ],
    
    // React Hooks 规则
    'react-hooks/rules-of-hooks': 'error',
    'react-hooks/exhaustive-deps': 'warn',
    
    // 强制函数复杂度限制
    'complexity': ['warn', 10],
    
    // 强制最大行数
    'max-lines': ['warn', 300],
    
    // 强制最大函数长度
    'max-lines-per-function': ['warn', 50]
  }
};
javascript
// prettier.config.js
module.exports = {
  semi: true,
  trailingComma: 'es5',
  singleQuote: true,
  printWidth: 80,
  tabWidth: 2,
  useTabs: false,
  bracketSpacing: true,
  arrowParens: 'avoid',
  endOfLine: 'lf'
};

代码注释和文档

javascript
/**
 * 用户认证服务
 * 提供登录、注册、令牌刷新等功能
 */
class AuthService {
  /**
   * 用户登录
   * @param {string} email - 用户邮箱
   * @param {string} password - 用户密码
   * @returns {Promise<AuthResult>} 认证结果
   * @throws {AuthError} 当认证失败时抛出
   * 
   * @example
   * ```javascript
   * try {
   *   const result = await authService.login('user@example.com', 'password');
   *   console.log('登录成功:', result.user);
   * } catch (error) {
   *   console.error('登录失败:', error.message);
   * }
   * ```
   */
  async login(email, password) {
    // 输入验证
    if (!email || !password) {
      throw new AuthError('邮箱和密码不能为空');
    }
    
    try {
      // 发送登录请求
      const response = await this.api.post('/auth/login', {
        email,
        password
      });
      
      // 存储令牌
      this.tokenManager.setTokens(response.data.tokens);
      
      return {
        user: response.data.user,
        tokens: response.data.tokens
      };
    } catch (error) {
      // 错误处理和日志记录
      this.logger.error('登录失败', { email, error: error.message });
      throw new AuthError('登录失败,请检查邮箱和密码');
    }
  }
  
  /**
   * 刷新访问令牌
   * @private
   * @returns {Promise<string>} 新的访问令牌
   */
  async refreshAccessToken() {
    // 私有方法实现
  }
}

错误处理

javascript
// 自定义错误类
class AppError extends Error {
  constructor(message, code, statusCode = 500, isOperational = true) {
    super(message);
    this.name = this.constructor.name;
    this.code = code;
    this.statusCode = statusCode;
    this.isOperational = isOperational;
    this.timestamp = new Date().toISOString();
    
    Error.captureStackTrace(this, this.constructor);
  }
}

class ValidationError extends AppError {
  constructor(message, field) {
    super(message, 'VALIDATION_ERROR', 400);
    this.field = field;
  }
}

class NotFoundError extends AppError {
  constructor(resource) {
    super(`${resource} not found`, 'NOT_FOUND', 404);
    this.resource = resource;
  }
}

// 错误处理工具
class ErrorHandler {
  static handle(error, context = {}) {
    // 记录错误
    logger.error('Error occurred', {
      message: error.message,
      stack: error.stack,
      code: error.code,
      context
    });
    
    // 根据错误类型返回适当的响应
    if (error instanceof ValidationError) {
      return {
        success: false,
        error: {
          type: 'validation',
          message: error.message,
          field: error.field
        }
      };
    }
    
    if (error instanceof NotFoundError) {
      return {
        success: false,
        error: {
          type: 'not_found',
          message: error.message,
          resource: error.resource
        }
      };
    }
    
    // 默认错误响应
    return {
      success: false,
      error: {
        type: 'internal',
        message: 'An unexpected error occurred'
      }
    };
  }
  
  static async withErrorHandling(fn, context = {}) {
    try {
      return await fn();
    } catch (error) {
      return this.handle(error, context);
    }
  }
}

// 使用示例
const userService = {
  async getUser(id) {
    return ErrorHandler.withErrorHandling(async () => {
      if (!id) {
        throw new ValidationError('User ID is required', 'id');
      }
      
      const user = await db.users.findById(id);
      
      if (!user) {
        throw new NotFoundError('User');
      }
      
      return { success: true, data: user };
    }, { operation: 'getUser', userId: id });
  }
};

性能优化

React 组件优化

javascript
import React, { memo, useMemo, useCallback, useState } from 'react';

// 使用 memo 防止不必要的重渲染
const UserCard = memo(({ user, onEdit, onDelete }) => {
  // 使用 useMemo 缓存计算结果
  const displayName = useMemo(() => {
    return `${user.firstName} ${user.lastName}`.trim();
  }, [user.firstName, user.lastName]);
  
  const userStats = useMemo(() => {
    return {
      totalPosts: user.posts?.length || 0,
      totalComments: user.comments?.length || 0,
      joinDate: new Date(user.createdAt).toLocaleDateString()
    };
  }, [user.posts, user.comments, user.createdAt]);
  
  // 使用 useCallback 缓存事件处理函数
  const handleEdit = useCallback(() => {
    onEdit(user.id);
  }, [user.id, onEdit]);
  
  const handleDelete = useCallback(() => {
    onDelete(user.id);
  }, [user.id, onDelete]);
  
  return (
    <div className="user-card">
      <h3>{displayName}</h3>
      <p>Posts: {userStats.totalPosts}</p>
      <p>Comments: {userStats.totalComments}</p>
      <p>Joined: {userStats.joinDate}</p>
      
      <div className="actions">
        <button onClick={handleEdit}>Edit</button>
        <button onClick={handleDelete}>Delete</button>
      </div>
    </div>
  );
});

// 自定义比较函数
const UserCardWithCustomComparison = memo(UserCard, (prevProps, nextProps) => {
  // 只有当用户数据真正改变时才重新渲染
  return (
    prevProps.user.id === nextProps.user.id &&
    prevProps.user.firstName === nextProps.user.firstName &&
    prevProps.user.lastName === nextProps.user.lastName &&
    prevProps.user.posts?.length === nextProps.user.posts?.length
  );
});

数据获取优化

javascript
// 数据缓存 Hook
function useCache(key, fetcher, options = {}) {
  const {
    ttl = 5 * 60 * 1000, // 5分钟
    staleWhileRevalidate = true
  } = options;
  
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  const cacheKey = `cache:${key}`;
  
  const fetchData = useCallback(async (force = false) => {
    try {
      // 检查缓存
      if (!force) {
        const cached = localStorage.getItem(cacheKey);
        if (cached) {
          const { data: cachedData, timestamp } = JSON.parse(cached);
          const isStale = Date.now() - timestamp > ttl;
          
          if (!isStale) {
            setData(cachedData);
            return cachedData;
          }
          
          if (staleWhileRevalidate) {
            setData(cachedData); // 返回过期数据
          }
        }
      }
      
      setLoading(true);
      setError(null);
      
      const result = await fetcher();
      
      // 更新缓存
      localStorage.setItem(cacheKey, JSON.stringify({
        data: result,
        timestamp: Date.now()
      }));
      
      setData(result);
      return result;
    } catch (err) {
      setError(err);
      throw err;
    } finally {
      setLoading(false);
    }
  }, [cacheKey, fetcher, ttl, staleWhileRevalidate]);
  
  useEffect(() => {
    fetchData();
  }, [fetchData]);
  
  const invalidate = useCallback(() => {
    localStorage.removeItem(cacheKey);
    fetchData(true);
  }, [cacheKey, fetchData]);
  
  return {
    data,
    loading,
    error,
    refetch: () => fetchData(true),
    invalidate
  };
}

// 使用示例
function UserList() {
  const {
    data: users,
    loading,
    error,
    refetch
  } = useCache(
    'users',
    () => api.get('/users'),
    { ttl: 10 * 60 * 1000 } // 10分钟缓存
  );
  
  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  
  return (
    <div>
      <button onClick={refetch}>Refresh</button>
      {users?.map(user => (
        <UserCard key={user.id} user={user} />
      ))}
    </div>
  );
}

虚拟化长列表

javascript
import { FixedSizeList as List } from 'react-window';

// 虚拟化列表组件
function VirtualizedUserList({ users }) {
  const Row = ({ index, style }) => {
    const user = users[index];
    
    return (
      <div style={style} className="user-row">
        <UserCard user={user} />
      </div>
    );
  };
  
  return (
    <List
      height={600}        // 容器高度
      itemCount={users.length}
      itemSize={120}      // 每行高度
      width="100%"
    >
      {Row}
    </List>
  );
}

// 动态高度的虚拟化列表
import { VariableSizeList as VariableList } from 'react-window';

function DynamicVirtualizedList({ items }) {
  const listRef = useRef();
  const rowHeights = useRef({});
  
  const getItemSize = useCallback((index) => {
    return rowHeights.current[index] || 80; // 默认高度
  }, []);
  
  const setItemSize = useCallback((index, size) => {
    if (rowHeights.current[index] !== size) {
      rowHeights.current[index] = size;
      if (listRef.current) {
        listRef.current.resetAfterIndex(index);
      }
    }
  }, []);
  
  const Row = ({ index, style }) => {
    const rowRef = useRef();
    
    useEffect(() => {
      if (rowRef.current) {
        const height = rowRef.current.getBoundingClientRect().height;
        setItemSize(index, height);
      }
    }, [index, setItemSize]);
    
    return (
      <div style={style}>
        <div ref={rowRef} className="dynamic-row">
          {/* 动态内容 */}
          {items[index]}
        </div>
      </div>
    );
  };
  
  return (
    <VariableList
      ref={listRef}
      height={600}
      itemCount={items.length}
      itemSize={getItemSize}
      width="100%"
    >
      {Row}
    </VariableList>
  );
}

安全最佳实践

输入验证和清理

javascript
// 输入验证工具
class Validator {
  static email(value) {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(value)) {
      throw new ValidationError('Invalid email format', 'email');
    }
    return value.toLowerCase().trim();
  }
  
  static password(value) {
    if (value.length < 8) {
      throw new ValidationError('Password must be at least 8 characters', 'password');
    }
    
    if (!/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])/.test(value)) {
      throw new ValidationError(
        'Password must contain uppercase, lowercase, number and special character',
        'password'
      );
    }
    
    return value;
  }
  
  static sanitizeHtml(value) {
    // 移除潜在的 XSS 攻击代码
    return value
      .replace(/<script[^>]*>.*?<\/script>/gi, '')
      .replace(/<iframe[^>]*>.*?<\/iframe>/gi, '')
      .replace(/javascript:/gi, '')
      .replace(/on\w+\s*=/gi, '');
  }
  
  static sqlSafe(value) {
    // 防止 SQL 注入
    if (typeof value !== 'string') return value;
    
    return value
      .replace(/'/g, "''")
      .replace(/;/g, '')
      .replace(/--/g, '')
      .replace(/\/\*/g, '')
      .replace(/\*\//g, '');
  }
}

// 使用示例
const userInput = {
  email: Validator.email(req.body.email),
  password: Validator.password(req.body.password),
  bio: Validator.sanitizeHtml(req.body.bio)
};

安全的 API 设计

javascript
// 速率限制中间件
const rateLimit = require('express-rate-limit');

const createRateLimiter = (options) => {
  return rateLimit({
    windowMs: options.windowMs || 15 * 60 * 1000, // 15分钟
    max: options.max || 100, // 最大请求数
    message: {
      error: 'Too many requests',
      retryAfter: Math.ceil(options.windowMs / 1000)
    },
    standardHeaders: true,
    legacyHeaders: false,
    keyGenerator: (req) => {
      // 基于用户 ID 或 IP 地址
      return req.user?.id || req.ip;
    },
    skip: (req) => {
      // 跳过某些请求
      return req.ip === '127.0.0.1' && process.env.NODE_ENV === 'development';
    }
  });
};

// 不同端点的不同限制
app.use('/api/auth/login', createRateLimiter({ max: 5, windowMs: 15 * 60 * 1000 }));
app.use('/api/auth/register', createRateLimiter({ max: 3, windowMs: 60 * 60 * 1000 }));
app.use('/api/', createRateLimiter({ max: 1000, windowMs: 15 * 60 * 1000 }));

// CSRF 保护
const csrf = require('csurf');

const csrfProtection = csrf({
  cookie: {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'strict'
  }
});

app.use(csrfProtection);

// 提供 CSRF 令牌
app.get('/api/csrf-token', (req, res) => {
  res.json({ csrfToken: req.csrfToken() });
});

敏感数据处理

javascript
// 数据脱敏工具
class DataMasker {
  static email(email) {
    const [username, domain] = email.split('@');
    const maskedUsername = username.length > 2 
      ? username[0] + '*'.repeat(username.length - 2) + username[username.length - 1]
      : '*'.repeat(username.length);
    return `${maskedUsername}@${domain}`;
  }
  
  static phone(phone) {
    return phone.replace(/(\d{3})(\d{4})(\d{4})/, '$1****$3');
  }
  
  static creditCard(cardNumber) {
    return cardNumber.replace(/(\d{4})(\d{4})(\d{4})(\d{4})/, '**** **** **** $4');
  }
  
  static idCard(idCard) {
    return idCard.replace(/(\d{6})(\d{8})(\d{4})/, '$1********$3');
  }
}

// 日志脱敏
class Logger {
  static info(message, data = {}) {
    const maskedData = this.maskSensitiveData(data);
    console.log(JSON.stringify({ level: 'info', message, data: maskedData }));
  }
  
  static error(message, error, data = {}) {
    const maskedData = this.maskSensitiveData(data);
    console.error(JSON.stringify({
      level: 'error',
      message,
      error: error.message,
      stack: error.stack,
      data: maskedData
    }));
  }
  
  static maskSensitiveData(data) {
    const sensitiveFields = ['password', 'token', 'secret', 'key', 'creditCard'];
    const masked = { ...data };
    
    for (const field of sensitiveFields) {
      if (masked[field]) {
        masked[field] = '***MASKED***';
      }
    }
    
    // 递归处理嵌套对象
    for (const [key, value] of Object.entries(masked)) {
      if (typeof value === 'object' && value !== null) {
        masked[key] = this.maskSensitiveData(value);
      }
    }
    
    return masked;
  }
}

测试策略

单元测试

javascript
// 测试工具设置
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { jest } from '@jest/globals';
import userEvent from '@testing-library/user-event';

// 组件测试示例
describe('UserCard Component', () => {
  const mockUser = {
    id: 1,
    firstName: 'John',
    lastName: 'Doe',
    email: 'john@example.com',
    posts: [{ id: 1 }, { id: 2 }],
    comments: [{ id: 1 }]
  };
  
  const mockOnEdit = jest.fn();
  const mockOnDelete = jest.fn();
  
  beforeEach(() => {
    jest.clearAllMocks();
  });
  
  it('should render user information correctly', () => {
    render(
      <UserCard 
        user={mockUser} 
        onEdit={mockOnEdit} 
        onDelete={mockOnDelete} 
      />
    );
    
    expect(screen.getByText('John Doe')).toBeInTheDocument();
    expect(screen.getByText('Posts: 2')).toBeInTheDocument();
    expect(screen.getByText('Comments: 1')).toBeInTheDocument();
  });
  
  it('should call onEdit when edit button is clicked', async () => {
    const user = userEvent.setup();
    
    render(
      <UserCard 
        user={mockUser} 
        onEdit={mockOnEdit} 
        onDelete={mockOnDelete} 
      />
    );
    
    const editButton = screen.getByRole('button', { name: /edit/i });
    await user.click(editButton);
    
    expect(mockOnEdit).toHaveBeenCalledWith(mockUser.id);
    expect(mockOnEdit).toHaveBeenCalledTimes(1);
  });
  
  it('should handle missing user data gracefully', () => {
    const incompleteUser = {
      id: 1,
      firstName: 'John'
      // lastName 缺失
    };
    
    render(
      <UserCard 
        user={incompleteUser} 
        onEdit={mockOnEdit} 
        onDelete={mockOnDelete} 
      />
    );
    
    expect(screen.getByText('John')).toBeInTheDocument();
    expect(screen.getByText('Posts: 0')).toBeInTheDocument();
  });
});

// Hook 测试
import { renderHook, act } from '@testing-library/react';

describe('useCache Hook', () => {
  const mockFetcher = jest.fn();
  
  beforeEach(() => {
    jest.clearAllMocks();
    localStorage.clear();
  });
  
  it('should fetch data on mount', async () => {
    const testData = { id: 1, name: 'Test' };
    mockFetcher.mockResolvedValue(testData);
    
    const { result } = renderHook(() => 
      useCache('test-key', mockFetcher)
    );
    
    expect(result.current.loading).toBe(true);
    
    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });
    
    expect(result.current.data).toEqual(testData);
    expect(mockFetcher).toHaveBeenCalledTimes(1);
  });
  
  it('should use cached data on subsequent calls', async () => {
    const testData = { id: 1, name: 'Test' };
    mockFetcher.mockResolvedValue(testData);
    
    // 第一次调用
    const { result, rerender } = renderHook(() => 
      useCache('test-key', mockFetcher)
    );
    
    await waitFor(() => {
      expect(result.current.data).toEqual(testData);
    });
    
    // 重新渲染,应该使用缓存
    rerender();
    
    expect(mockFetcher).toHaveBeenCalledTimes(1); // 仍然只调用一次
  });
});

集成测试

javascript
// API 集成测试
import request from 'supertest';
import app from '../src/app.js';
import { setupTestDb, cleanupTestDb } from './helpers/database.js';

describe('User API Integration Tests', () => {
  let authToken;
  let testUser;
  
  beforeAll(async () => {
    await setupTestDb();
  });
  
  afterAll(async () => {
    await cleanupTestDb();
  });
  
  beforeEach(async () => {
    // 创建测试用户并获取认证令牌
    const response = await request(app)
      .post('/api/auth/register')
      .send({
        email: 'test@example.com',
        password: 'TestPassword123!',
        firstName: 'Test',
        lastName: 'User'
      });
    
    testUser = response.body.user;
    authToken = response.body.tokens.accessToken;
  });
  
  describe('GET /api/users/:id', () => {
    it('should return user data for authenticated request', async () => {
      const response = await request(app)
        .get(`/api/users/${testUser.id}`)
        .set('Authorization', `Bearer ${authToken}`)
        .expect(200);
      
      expect(response.body.success).toBe(true);
      expect(response.body.data.id).toBe(testUser.id);
      expect(response.body.data.email).toBe('test@example.com');
      // 密码不应该在响应中
      expect(response.body.data.password).toBeUndefined();
    });
    
    it('should return 401 for unauthenticated request', async () => {
      await request(app)
        .get(`/api/users/${testUser.id}`)
        .expect(401);
    });
    
    it('should return 404 for non-existent user', async () => {
      await request(app)
        .get('/api/users/99999')
        .set('Authorization', `Bearer ${authToken}`)
        .expect(404);
    });
  });
  
  describe('PUT /api/users/:id', () => {
    it('should update user data', async () => {
      const updateData = {
        firstName: 'Updated',
        lastName: 'Name'
      };
      
      const response = await request(app)
        .put(`/api/users/${testUser.id}`)
        .set('Authorization', `Bearer ${authToken}`)
        .send(updateData)
        .expect(200);
      
      expect(response.body.success).toBe(true);
      expect(response.body.data.firstName).toBe('Updated');
      expect(response.body.data.lastName).toBe('Name');
    });
    
    it('should validate input data', async () => {
      const invalidData = {
        email: 'invalid-email'
      };
      
      const response = await request(app)
        .put(`/api/users/${testUser.id}`)
        .set('Authorization', `Bearer ${authToken}`)
        .send(invalidData)
        .expect(400);
      
      expect(response.body.success).toBe(false);
      expect(response.body.error.type).toBe('validation');
    });
  });
});

端到端测试

javascript
// Playwright E2E 测试
import { test, expect } from '@playwright/test';

test.describe('User Management Flow', () => {
  test.beforeEach(async ({ page }) => {
    // 导航到应用
    await page.goto('http://localhost:3000');
  });
  
  test('should complete user registration and login flow', async ({ page }) => {
    // 注册新用户
    await page.click('text=Sign Up');
    await page.fill('[data-testid="email-input"]', 'test@example.com');
    await page.fill('[data-testid="password-input"]', 'TestPassword123!');
    await page.fill('[data-testid="confirm-password-input"]', 'TestPassword123!');
    await page.fill('[data-testid="first-name-input"]', 'Test');
    await page.fill('[data-testid="last-name-input"]', 'User');
    
    await page.click('[data-testid="register-button"]');
    
    // 验证注册成功
    await expect(page.locator('text=Registration successful')).toBeVisible();
    
    // 登录
    await page.click('text=Sign In');
    await page.fill('[data-testid="login-email"]', 'test@example.com');
    await page.fill('[data-testid="login-password"]', 'TestPassword123!');
    await page.click('[data-testid="login-button"]');
    
    // 验证登录成功
    await expect(page.locator('[data-testid="user-menu"]')).toBeVisible();
    await expect(page.locator('text=Welcome, Test')).toBeVisible();
  });
  
  test('should handle form validation errors', async ({ page }) => {
    await page.click('text=Sign Up');
    
    // 提交空表单
    await page.click('[data-testid="register-button"]');
    
    // 验证错误消息
    await expect(page.locator('text=Email is required')).toBeVisible();
    await expect(page.locator('text=Password is required')).toBeVisible();
    
    // 输入无效邮箱
    await page.fill('[data-testid="email-input"]', 'invalid-email');
    await page.click('[data-testid="register-button"]');
    
    await expect(page.locator('text=Please enter a valid email')).toBeVisible();
  });
  
  test('should navigate through user profile pages', async ({ page }) => {
    // 假设用户已登录
    await page.goto('http://localhost:3000/login');
    await page.fill('[data-testid="login-email"]', 'existing@example.com');
    await page.fill('[data-testid="login-password"]', 'password123');
    await page.click('[data-testid="login-button"]');
    
    // 导航到用户资料
    await page.click('[data-testid="user-menu"]');
    await page.click('text=Profile');
    
    // 验证资料页面
    await expect(page.locator('[data-testid="profile-page"]')).toBeVisible();
    
    // 编辑资料
    await page.click('[data-testid="edit-profile-button"]');
    await page.fill('[data-testid="bio-input"]', 'Updated bio text');
    await page.click('[data-testid="save-profile-button"]');
    
    // 验证更新成功
    await expect(page.locator('text=Profile updated successfully')).toBeVisible();
    await expect(page.locator('text=Updated bio text')).toBeVisible();
  });
});

部署和运维

环境配置

javascript
// 环境配置管理
class EnvironmentConfig {
  constructor() {
    this.env = process.env.NODE_ENV || 'development';
    this.loadConfig();
  }
  
  loadConfig() {
    const baseConfig = {
      app: {
        name: process.env.APP_NAME || 'Trae App',
        version: process.env.APP_VERSION || '1.0.0',
        port: parseInt(process.env.PORT) || 3000
      },
      database: {
        host: process.env.DB_HOST || 'localhost',
        port: parseInt(process.env.DB_PORT) || 5432,
        name: process.env.DB_NAME,
        user: process.env.DB_USER,
        password: process.env.DB_PASSWORD
      }
    };
    
    const envConfigs = {
      development: {
        ...baseConfig,
        logging: {
          level: 'debug',
          console: true,
          file: false
        },
        cache: {
          enabled: false
        }
      },
      
      staging: {
        ...baseConfig,
        logging: {
          level: 'info',
          console: true,
          file: true
        },
        cache: {
          enabled: true,
          ttl: 300 // 5分钟
        }
      },
      
      production: {
        ...baseConfig,
        logging: {
          level: 'warn',
          console: false,
          file: true,
          elasticsearch: true
        },
        cache: {
          enabled: true,
          ttl: 3600 // 1小时
        },
        security: {
          helmet: true,
          cors: {
            origin: process.env.ALLOWED_ORIGINS?.split(',') || []
          }
        }
      }
    };
    
    this.config = envConfigs[this.env] || envConfigs.development;
  }
  
  get(path) {
    return path.split('.').reduce((obj, key) => obj?.[key], this.config);
  }
  
  isDevelopment() {
    return this.env === 'development';
  }
  
  isProduction() {
    return this.env === 'production';
  }
}

const config = new EnvironmentConfig();
export default config;

健康检查和监控

javascript
// 健康检查服务
class HealthCheckService {
  constructor() {
    this.checks = new Map();
    this.registerDefaultChecks();
  }
  
  registerCheck(name, checkFunction, options = {}) {
    this.checks.set(name, {
      check: checkFunction,
      timeout: options.timeout || 5000,
      critical: options.critical || false
    });
  }
  
  registerDefaultChecks() {
    // 数据库连接检查
    this.registerCheck('database', async () => {
      const result = await db.query('SELECT 1');
      return { status: 'ok', response_time: Date.now() };
    }, { critical: true });
    
    // Redis 连接检查
    this.registerCheck('redis', async () => {
      const start = Date.now();
      await redis.ping();
      return { 
        status: 'ok', 
        response_time: Date.now() - start 
      };
    });
    
    // 磁盘空间检查
    this.registerCheck('disk_space', async () => {
      const stats = await fs.promises.statfs('.');
      const freeSpace = stats.bavail * stats.bsize;
      const totalSpace = stats.blocks * stats.bsize;
      const usagePercent = ((totalSpace - freeSpace) / totalSpace) * 100;
      
      return {
        status: usagePercent < 90 ? 'ok' : 'warning',
        usage_percent: usagePercent,
        free_space_gb: Math.round(freeSpace / 1024 / 1024 / 1024)
      };
    });
    
    // 内存使用检查
    this.registerCheck('memory', async () => {
      const usage = process.memoryUsage();
      const usageMB = Math.round(usage.heapUsed / 1024 / 1024);
      
      return {
        status: usageMB < 500 ? 'ok' : 'warning',
        heap_used_mb: usageMB,
        heap_total_mb: Math.round(usage.heapTotal / 1024 / 1024)
      };
    });
  }
  
  async runCheck(name) {
    const checkConfig = this.checks.get(name);
    if (!checkConfig) {
      throw new Error(`Health check '${name}' not found`);
    }
    
    const start = Date.now();
    
    try {
      const result = await Promise.race([
        checkConfig.check(),
        new Promise((_, reject) => 
          setTimeout(() => reject(new Error('Timeout')), checkConfig.timeout)
        )
      ]);
      
      return {
        name,
        status: result.status || 'ok',
        duration: Date.now() - start,
        details: result,
        timestamp: new Date().toISOString()
      };
    } catch (error) {
      return {
        name,
        status: 'error',
        duration: Date.now() - start,
        error: error.message,
        critical: checkConfig.critical,
        timestamp: new Date().toISOString()
      };
    }
  }
  
  async runAllChecks() {
    const checkNames = Array.from(this.checks.keys());
    const results = await Promise.all(
      checkNames.map(name => this.runCheck(name))
    );
    
    const overallStatus = results.some(r => r.status === 'error' && r.critical)
      ? 'error'
      : results.some(r => r.status === 'error' || r.status === 'warning')
      ? 'warning'
      : 'ok';
    
    return {
      status: overallStatus,
      timestamp: new Date().toISOString(),
      checks: results,
      summary: {
        total: results.length,
        passed: results.filter(r => r.status === 'ok').length,
        warnings: results.filter(r => r.status === 'warning').length,
        errors: results.filter(r => r.status === 'error').length
      }
    };
  }
}

const healthCheck = new HealthCheckService();

// 健康检查端点
app.get('/health', async (req, res) => {
  try {
    const result = await healthCheck.runAllChecks();
    const statusCode = result.status === 'ok' ? 200 : 503;
    res.status(statusCode).json(result);
  } catch (error) {
    res.status(500).json({
      status: 'error',
      error: error.message,
      timestamp: new Date().toISOString()
    });
  }
});

优雅关闭

javascript
// 优雅关闭处理
class GracefulShutdown {
  constructor() {
    this.shutdownHandlers = [];
    this.isShuttingDown = false;
    this.setupSignalHandlers();
  }
  
  addShutdownHandler(name, handler, timeout = 10000) {
    this.shutdownHandlers.push({ name, handler, timeout });
  }
  
  setupSignalHandlers() {
    const signals = ['SIGTERM', 'SIGINT', 'SIGUSR2'];
    
    signals.forEach(signal => {
      process.on(signal, () => {
        console.log(`Received ${signal}, starting graceful shutdown...`);
        this.shutdown();
      });
    });
    
    process.on('uncaughtException', (error) => {
      console.error('Uncaught Exception:', error);
      this.shutdown(1);
    });
    
    process.on('unhandledRejection', (reason, promise) => {
      console.error('Unhandled Rejection at:', promise, 'reason:', reason);
      this.shutdown(1);
    });
  }
  
  async shutdown(exitCode = 0) {
    if (this.isShuttingDown) {
      console.log('Shutdown already in progress...');
      return;
    }
    
    this.isShuttingDown = true;
    console.log('Starting graceful shutdown...');
    
    // 停止接受新连接
    if (this.server) {
      this.server.close(() => {
        console.log('HTTP server closed');
      });
    }
    
    // 执行关闭处理程序
    for (const { name, handler, timeout } of this.shutdownHandlers) {
      try {
        console.log(`Running shutdown handler: ${name}`);
        
        await Promise.race([
          handler(),
          new Promise((_, reject) => 
            setTimeout(() => reject(new Error(`Timeout: ${name}`)), timeout)
          )
        ]);
        
        console.log(`Shutdown handler completed: ${name}`);
      } catch (error) {
        console.error(`Shutdown handler failed: ${name}`, error);
      }
    }
    
    console.log('Graceful shutdown completed');
    process.exit(exitCode);
  }
  
  setServer(server) {
    this.server = server;
  }
}

const gracefulShutdown = new GracefulShutdown();

// 注册关闭处理程序
gracefulShutdown.addShutdownHandler('database', async () => {
  await db.end();
  console.log('Database connections closed');
});

gracefulShutdown.addShutdownHandler('redis', async () => {
  await redis.quit();
  console.log('Redis connection closed');
});

gracefulShutdown.addShutdownHandler('cleanup', async () => {
  // 清理临时文件、缓存等
  await cleanupTempFiles();
  console.log('Cleanup completed');
});

// 启动服务器
const server = app.listen(config.get('app.port'), () => {
  console.log(`Server running on port ${config.get('app.port')}`);
});

gracefulShutdown.setServer(server);

这个最佳实践指南涵盖了 Trae 开发的各个方面,从项目结构到部署运维。遵循这些实践可以帮助您构建高质量、可维护、安全的应用程序。记住,最佳实践会随着技术发展而演进,建议定期回顾和更新您的开发流程。

您的终极 AI 驱动 IDE 学习指南