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