Skip to content

项目开发

Trae IDE 提供了完整的项目开发生命周期支持,从项目创建到部署上线,每个环节都经过精心设计,帮助开发者高效构建现代化应用程序。

项目创建

项目模板

Web 应用模板

bash
# React 项目
trae create my-react-app --template react
# 包含:React 18, TypeScript, Vite, ESLint, Prettier

# Vue 项目
trae create my-vue-app --template vue
# 包含:Vue 3, TypeScript, Vite, Vue Router, Pinia

# Angular 项目
trae create my-angular-app --template angular
# 包含:Angular 17, TypeScript, Angular CLI, RxJS

# Next.js 项目
trae create my-nextjs-app --template nextjs
# 包含:Next.js 14, TypeScript, Tailwind CSS, App Router

后端服务模板

bash
# Node.js API
trae create my-api --template node-api
# 包含:Express, TypeScript, Prisma, JWT 认证

# Python FastAPI
trae create my-fastapi --template fastapi
# 包含:FastAPI, SQLAlchemy, Alembic, Pydantic

# Java Spring Boot
trae create my-spring-app --template spring-boot
# 包含:Spring Boot 3, Spring Security, JPA, Maven

# Go Gin API
trae create my-go-api --template go-gin
# 包含:Gin, GORM, JWT, Go Modules

全栈项目模板

bash
# MERN Stack
trae create my-mern-app --template mern
# 前端:React + TypeScript + Vite
# 后端:Node.js + Express + MongoDB

# T3 Stack
trae create my-t3-app --template t3
# Next.js + TypeScript + tRPC + Prisma + NextAuth

# Django + React
trae create my-django-react --template django-react
# 后端:Django + DRF + PostgreSQL
# 前端:React + TypeScript + Vite

自定义项目结构

项目配置文件

json
// trae.config.json
{
  "name": "my-awesome-project",
  "version": "1.0.0",
  "type": "fullstack",
  "structure": {
    "frontend": {
      "framework": "react",
      "language": "typescript",
      "bundler": "vite",
      "styling": "tailwindcss"
    },
    "backend": {
      "framework": "express",
      "language": "typescript",
      "database": "postgresql",
      "orm": "prisma"
    },
    "shared": {
      "types": "./shared/types",
      "utils": "./shared/utils",
      "constants": "./shared/constants"
    }
  },
  "scripts": {
    "dev": "concurrently \"npm run dev:frontend\" \"npm run dev:backend\"",
    "build": "npm run build:frontend && npm run build:backend",
    "test": "npm run test:frontend && npm run test:backend"
  }
}

目录结构生成

my-awesome-project/
├── frontend/
│   ├── src/
│   │   ├── components/
│   │   ├── pages/
│   │   ├── hooks/
│   │   ├── utils/
│   │   └── types/
│   ├── public/
│   ├── package.json
│   └── vite.config.ts
├── backend/
│   ├── src/
│   │   ├── controllers/
│   │   ├── services/
│   │   ├── models/
│   │   ├── middleware/
│   │   └── routes/
│   ├── prisma/
│   ├── package.json
│   └── tsconfig.json
├── shared/
│   ├── types/
│   ├── utils/
│   └── constants/
├── docs/
├── docker-compose.yml
└── README.md

开发工作流

智能代码生成

组件生成

jsx
// 输入:"创建用户列表组件"
// AI 生成:
import React, { useState, useEffect } from 'react';
import { User } from '../types/User';
import { userService } from '../services/userService';

interface UserListProps {
  onUserSelect?: (user: User) => void;
  searchTerm?: string;
}

const UserList: React.FC<UserListProps> = ({ onUserSelect, searchTerm }) => {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const fetchUsers = async () => {
      try {
        setLoading(true);
        const userData = await userService.getUsers(searchTerm);
        setUsers(userData);
      } catch (err) {
        setError('Failed to fetch users');
      } finally {
        setLoading(false);
      }
    };

    fetchUsers();
  }, [searchTerm]);

  if (loading) return <div className="loading">Loading users...</div>;
  if (error) return <div className="error">{error}</div>;

  return (
    <div className="user-list">
      {users.map(user => (
        <div 
          key={user.id} 
          className="user-item"
          onClick={() => onUserSelect?.(user)}
        >
          <img src={user.avatar} alt={user.name} className="user-avatar" />
          <div className="user-info">
            <h3>{user.name}</h3>
            <p>{user.email}</p>
          </div>
        </div>
      ))}
    </div>
  );
};

export default UserList;

API 路由生成

typescript
// 输入:"创建用户管理 API"
// AI 生成:
import { Router, Request, Response } from 'express';
import { UserService } from '../services/UserService';
import { CreateUserDto, UpdateUserDto } from '../dto/UserDto';
import { authMiddleware } from '../middleware/auth';
import { validateDto } from '../middleware/validation';

const router = Router();
const userService = new UserService();

// 获取所有用户
router.get('/', authMiddleware, async (req: Request, res: Response) => {
  try {
    const { page = 1, limit = 10, search } = req.query;
    const users = await userService.getUsers({
      page: Number(page),
      limit: Number(limit),
      search: search as string
    });
    res.json(users);
  } catch (error) {
    res.status(500).json({ error: 'Failed to fetch users' });
  }
});

// 获取单个用户
router.get('/:id', authMiddleware, async (req: Request, res: Response) => {
  try {
    const user = await userService.getUserById(req.params.id);
    if (!user) {
      return res.status(404).json({ error: 'User not found' });
    }
    res.json(user);
  } catch (error) {
    res.status(500).json({ error: 'Failed to fetch user' });
  }
});

// 创建用户
router.post('/', 
  authMiddleware, 
  validateDto(CreateUserDto),
  async (req: Request, res: Response) => {
    try {
      const user = await userService.createUser(req.body);
      res.status(201).json(user);
    } catch (error) {
      res.status(400).json({ error: 'Failed to create user' });
    }
  }
);

// 更新用户
router.put('/:id', 
  authMiddleware, 
  validateDto(UpdateUserDto),
  async (req: Request, res: Response) => {
    try {
      const user = await userService.updateUser(req.params.id, req.body);
      res.json(user);
    } catch (error) {
      res.status(400).json({ error: 'Failed to update user' });
    }
  }
);

// 删除用户
router.delete('/:id', authMiddleware, async (req: Request, res: Response) => {
  try {
    await userService.deleteUser(req.params.id);
    res.status(204).send();
  } catch (error) {
    res.status(400).json({ error: 'Failed to delete user' });
  }
});

export default router;

实时协作开发

Live Share 功能

bash
# 启动协作会话
trae share start
# 生成分享链接:https://trae.ai/share/abc123

# 加入协作会话
trae share join abc123

# 协作功能
- 实时代码同步
- 共享终端访问
- 协作调试
- 语音/视频通话
- 共享服务器端口

代码审查集成

javascript
// 代码审查配置
{
  "codeReview": {
    "enabled": true,
    "autoAssign": true,
    "reviewers": ["@team-lead", "@senior-dev"],
    "rules": {
      "minReviewers": 2,
      "requireApproval": true,
      "blockMergeOnFailedChecks": true
    },
    "checks": {
      "linting": true,
      "testing": true,
      "security": true,
      "performance": true
    }
  }
}

构建系统

现代构建工具

Vite 配置

typescript
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src'),
      '@components': resolve(__dirname, 'src/components'),
      '@utils': resolve(__dirname, 'src/utils'),
      '@types': resolve(__dirname, 'src/types')
    }
  },
  build: {
    target: 'esnext',
    minify: 'esbuild',
    sourcemap: true,
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          router: ['react-router-dom'],
          ui: ['@mui/material', '@emotion/react']
        }
      }
    }
  },
  server: {
    port: 3000,
    proxy: {
      '/api': {
        target: 'http://localhost:8000',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
});

Webpack 配置(高级项目)

javascript
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

module.exports = (env, argv) => {
  const isProduction = argv.mode === 'production';
  
  return {
    entry: './src/index.tsx',
    output: {
      path: path.resolve(__dirname, 'dist'),
      filename: isProduction 
        ? '[name].[contenthash].js' 
        : '[name].js',
      clean: true
    },
    resolve: {
      extensions: ['.tsx', '.ts', '.js'],
      alias: {
        '@': path.resolve(__dirname, 'src')
      }
    },
    module: {
      rules: [
        {
          test: /\.(ts|tsx)$/,
          use: 'ts-loader',
          exclude: /node_modules/
        },
        {
          test: /\.css$/,
          use: [
            isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
            'css-loader',
            'postcss-loader'
          ]
        }
      ]
    },
    plugins: [
      new HtmlWebpackPlugin({
        template: './public/index.html'
      }),
      ...(isProduction ? [
        new MiniCssExtractPlugin({
          filename: '[name].[contenthash].css'
        }),
        new BundleAnalyzerPlugin({
          analyzerMode: 'static',
          openAnalyzer: false
        })
      ] : [])
    ],
    optimization: {
      splitChunks: {
        chunks: 'all',
        cacheGroups: {
          vendor: {
            test: /[\\/]node_modules[\\/]/,
            name: 'vendors',
            chunks: 'all'
          }
        }
      }
    },
    devServer: {
      port: 3000,
      hot: true,
      historyApiFallback: true
    }
  };
};

构建优化

代码分割

typescript
// 路由级代码分割
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';

const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/dashboard" element={<Dashboard />} />
      </Routes>
    </Suspense>
  );
}

资源优化

javascript
// 图片优化配置
{
  test: /\.(png|jpe?g|gif|svg)$/i,
  type: 'asset',
  parser: {
    dataUrlCondition: {
      maxSize: 8 * 1024 // 8KB
    }
  },
  generator: {
    filename: 'images/[name].[hash][ext]'
  },
  use: [
    {
      loader: 'image-webpack-loader',
      options: {
        mozjpeg: { progressive: true, quality: 80 },
        optipng: { enabled: false },
        pngquant: { quality: [0.65, 0.90], speed: 4 },
        gifsicle: { interlaced: false },
        webp: { quality: 80 }
      }
    }
  ]
}

测试集成

单元测试

Jest + React Testing Library

typescript
// UserList.test.tsx
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { UserList } from './UserList';
import { userService } from '../services/userService';

// Mock 服务
jest.mock('../services/userService');
const mockUserService = userService as jest.Mocked<typeof userService>;

const mockUsers = [
  { id: '1', name: 'Alice', email: 'alice@example.com' },
  { id: '2', name: 'Bob', email: 'bob@example.com' }
];

describe('UserList', () => {
  beforeEach(() => {
    mockUserService.getUsers.mockResolvedValue(mockUsers);
  });

  afterEach(() => {
    jest.clearAllMocks();
  });

  it('renders user list correctly', async () => {
    render(<UserList />);
    
    // 等待数据加载
    await waitFor(() => {
      expect(screen.getByText('Alice')).toBeInTheDocument();
      expect(screen.getByText('Bob')).toBeInTheDocument();
    });
  });

  it('calls onUserSelect when user is clicked', async () => {
    const onUserSelect = jest.fn();
    render(<UserList onUserSelect={onUserSelect} />);
    
    await waitFor(() => {
      expect(screen.getByText('Alice')).toBeInTheDocument();
    });
    
    await userEvent.click(screen.getByText('Alice'));
    expect(onUserSelect).toHaveBeenCalledWith(mockUsers[0]);
  });

  it('displays loading state', () => {
    mockUserService.getUsers.mockImplementation(
      () => new Promise(resolve => setTimeout(resolve, 1000))
    );
    
    render(<UserList />);
    expect(screen.getByText('Loading users...')).toBeInTheDocument();
  });

  it('displays error state', async () => {
    mockUserService.getUsers.mockRejectedValue(new Error('API Error'));
    
    render(<UserList />);
    
    await waitFor(() => {
      expect(screen.getByText('Failed to fetch users')).toBeInTheDocument();
    });
  });
});

后端测试

typescript
// userService.test.ts
import { UserService } from './UserService';
import { UserRepository } from '../repositories/UserRepository';
import { CreateUserDto } from '../dto/UserDto';

jest.mock('../repositories/UserRepository');
const mockUserRepository = UserRepository as jest.MockedClass<typeof UserRepository>;

describe('UserService', () => {
  let userService: UserService;
  let userRepository: jest.Mocked<UserRepository>;

  beforeEach(() => {
    userRepository = new mockUserRepository() as jest.Mocked<UserRepository>;
    userService = new UserService(userRepository);
  });

  describe('createUser', () => {
    it('should create user successfully', async () => {
      const createUserDto: CreateUserDto = {
        name: 'John Doe',
        email: 'john@example.com',
        password: 'password123'
      };

      const expectedUser = {
        id: '1',
        ...createUserDto,
        createdAt: new Date()
      };

      userRepository.create.mockResolvedValue(expectedUser);
      userRepository.findByEmail.mockResolvedValue(null);

      const result = await userService.createUser(createUserDto);

      expect(userRepository.findByEmail).toHaveBeenCalledWith(createUserDto.email);
      expect(userRepository.create).toHaveBeenCalledWith(createUserDto);
      expect(result).toEqual(expectedUser);
    });

    it('should throw error if email already exists', async () => {
      const createUserDto: CreateUserDto = {
        name: 'John Doe',
        email: 'john@example.com',
        password: 'password123'
      };

      userRepository.findByEmail.mockResolvedValue({ id: '1' } as any);

      await expect(userService.createUser(createUserDto))
        .rejects
        .toThrow('Email already exists');

      expect(userRepository.create).not.toHaveBeenCalled();
    });
  });
});

端到端测试

Playwright 测试

typescript
// e2e/user-management.spec.ts
import { test, expect } from '@playwright/test';

test.describe('User Management', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/users');
  });

  test('should display user list', async ({ page }) => {
    await expect(page.locator('.user-list')).toBeVisible();
    await expect(page.locator('.user-item')).toHaveCount(3);
  });

  test('should create new user', async ({ page }) => {
    await page.click('[data-testid="add-user-button"]');
    
    await page.fill('[data-testid="name-input"]', 'New User');
    await page.fill('[data-testid="email-input"]', 'newuser@example.com');
    await page.fill('[data-testid="password-input"]', 'password123');
    
    await page.click('[data-testid="submit-button"]');
    
    await expect(page.locator('text=User created successfully')).toBeVisible();
    await expect(page.locator('text=New User')).toBeVisible();
  });

  test('should edit user', async ({ page }) => {
    await page.click('[data-testid="user-1"] [data-testid="edit-button"]');
    
    await page.fill('[data-testid="name-input"]', 'Updated Name');
    await page.click('[data-testid="save-button"]');
    
    await expect(page.locator('text=User updated successfully')).toBeVisible();
    await expect(page.locator('text=Updated Name')).toBeVisible();
  });

  test('should delete user', async ({ page }) => {
    await page.click('[data-testid="user-1"] [data-testid="delete-button"]');
    await page.click('[data-testid="confirm-delete"]');
    
    await expect(page.locator('text=User deleted successfully')).toBeVisible();
    await expect(page.locator('[data-testid="user-1"]')).not.toBeVisible();
  });
});

测试配置

Jest 配置

javascript
// jest.config.js
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'],
  moduleNameMapping: {
    '^@/(.*)$': '<rootDir>/src/$1',
    '\\.(css|less|scss|sass)$': 'identity-obj-proxy'
  },
  collectCoverageFrom: [
    'src/**/*.{ts,tsx}',
    '!src/**/*.d.ts',
    '!src/index.tsx',
    '!src/reportWebVitals.ts'
  ],
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80
    }
  },
  testMatch: [
    '<rootDir>/src/**/__tests__/**/*.{ts,tsx}',
    '<rootDir>/src/**/*.{test,spec}.{ts,tsx}'
  ]
};

包管理

依赖管理

package.json 优化

json
{
  "name": "my-awesome-app",
  "version": "1.0.0",
  "private": true,
  "engines": {
    "node": ">=18.0.0",
    "npm": ">=8.0.0"
  },
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage",
    "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "lint:fix": "eslint src --ext ts,tsx --fix",
    "type-check": "tsc --noEmit",
    "prepare": "husky install"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.8.0",
    "@tanstack/react-query": "^4.24.0",
    "axios": "^1.3.0",
    "zustand": "^4.3.0"
  },
  "devDependencies": {
    "@types/react": "^18.0.27",
    "@types/react-dom": "^18.0.10",
    "@vitejs/plugin-react": "^3.1.0",
    "typescript": "^4.9.4",
    "vite": "^4.1.0",
    "eslint": "^8.34.0",
    "prettier": "^2.8.0",
    "husky": "^8.0.0",
    "lint-staged": "^13.1.0"
  },
  "lint-staged": {
    "*.{ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ]
  }
}

Monorepo 管理

json
// lerna.json
{
  "version": "independent",
  "npmClient": "npm",
  "command": {
    "publish": {
      "conventionalCommits": true,
      "message": "chore(release): publish",
      "registry": "https://registry.npmjs.org/"
    },
    "bootstrap": {
      "ignore": "component-*",
      "npmClientArgs": ["--no-package-lock"]
    }
  },
  "packages": [
    "packages/*"
  ]
}

// 工作区结构
packages/
├── shared/           # 共享工具和类型
├── ui-components/    # UI 组件库
├── web-app/         # Web 应用
├── mobile-app/      # 移动应用
└── api-server/      # API 服务

版本管理

语义化版本

bash
# 自动版本管理
npm run release:patch  # 1.0.0 -> 1.0.1
npm run release:minor  # 1.0.0 -> 1.1.0
npm run release:major  # 1.0.0 -> 2.0.0

# 预发布版本
npm run release:alpha  # 1.0.0 -> 1.0.1-alpha.0
npm run release:beta   # 1.0.0 -> 1.0.1-beta.0
npm run release:rc     # 1.0.0 -> 1.0.1-rc.0

Changelog 生成

markdown
# Changelog

## [1.2.0] - 2024-01-15

### Added
- 新增用户管理功能
- 添加暗色主题支持
- 集成 AI 代码补全

### Changed
- 优化页面加载性能
- 更新 UI 组件库到 v2.0
- 改进错误处理机制

### Fixed
- 修复登录状态丢失问题
- 解决移动端布局异常
- 修复内存泄漏问题

### Security
- 升级依赖包修复安全漏洞
- 加强 API 访问控制

数据库集成

ORM 配置

Prisma 配置

prisma
// schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id        String   @id @default(cuid())
  email     String   @unique
  name      String?
  avatar    String?
  role      Role     @default(USER)
  posts     Post[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@map("users")
}

model Post {
  id        String   @id @default(cuid())
  title     String
  content   String?
  published Boolean  @default(false)
  author    User     @relation(fields: [authorId], references: [id])
  authorId  String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@map("posts")
}

enum Role {
  USER
  ADMIN
}

数据库服务

typescript
// services/DatabaseService.ts
import { PrismaClient } from '@prisma/client';

class DatabaseService {
  private prisma: PrismaClient;

  constructor() {
    this.prisma = new PrismaClient({
      log: ['query', 'info', 'warn', 'error'],
    });
  }

  async connect() {
    try {
      await this.prisma.$connect();
      console.log('Database connected successfully');
    } catch (error) {
      console.error('Database connection failed:', error);
      throw error;
    }
  }

  async disconnect() {
    await this.prisma.$disconnect();
  }

  get client() {
    return this.prisma;
  }

  async healthCheck() {
    try {
      await this.prisma.$queryRaw`SELECT 1`;
      return { status: 'healthy', timestamp: new Date() };
    } catch (error) {
      return { status: 'unhealthy', error: error.message, timestamp: new Date() };
    }
  }
}

export const databaseService = new DatabaseService();

数据迁移

迁移脚本

sql
-- migrations/001_initial_schema.sql
CREATE TABLE "users" (
    "id" TEXT NOT NULL,
    "email" TEXT NOT NULL,
    "name" TEXT,
    "avatar" TEXT,
    "role" "Role" NOT NULL DEFAULT 'USER',
    "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "updatedAt" TIMESTAMP(3) NOT NULL,

    CONSTRAINT "users_pkey" PRIMARY KEY ("id")
);

CREATE TABLE "posts" (
    "id" TEXT NOT NULL,
    "title" TEXT NOT NULL,
    "content" TEXT,
    "published" BOOLEAN NOT NULL DEFAULT false,
    "authorId" TEXT NOT NULL,
    "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "updatedAt" TIMESTAMP(3) NOT NULL,

    CONSTRAINT "posts_pkey" PRIMARY KEY ("id")
);

CREATE UNIQUE INDEX "users_email_key" ON "users"("email");

ALTER TABLE "posts" ADD CONSTRAINT "posts_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

种子数据

typescript
// prisma/seed.ts
import { PrismaClient } from '@prisma/client';
import bcrypt from 'bcryptjs';

const prisma = new PrismaClient();

async function main() {
  // 创建管理员用户
  const adminUser = await prisma.user.upsert({
    where: { email: 'admin@example.com' },
    update: {},
    create: {
      email: 'admin@example.com',
      name: 'Admin User',
      role: 'ADMIN',
    },
  });

  // 创建示例文章
  const post1 = await prisma.post.upsert({
    where: { id: 'post-1' },
    update: {},
    create: {
      id: 'post-1',
      title: 'Welcome to Trae IDE',
      content: 'This is your first post created with Trae IDE.',
      published: true,
      authorId: adminUser.id,
    },
  });

  console.log({ adminUser, post1 });
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });

API 开发

RESTful API 设计

API 路由结构

typescript
// routes/api.ts
import { Router } from 'express';
import userRoutes from './users';
import postRoutes from './posts';
import authRoutes from './auth';
import { authMiddleware } from '../middleware/auth';
import { rateLimitMiddleware } from '../middleware/rateLimit';

const router = Router();

// 公开路由
router.use('/auth', rateLimitMiddleware, authRoutes);

// 受保护的路由
router.use('/users', authMiddleware, userRoutes);
router.use('/posts', authMiddleware, postRoutes);

// API 健康检查
router.get('/health', (req, res) => {
  res.json({
    status: 'ok',
    timestamp: new Date().toISOString(),
    version: process.env.npm_package_version
  });
});

export default router;

API 文档生成

typescript
// swagger.ts
import swaggerJsdoc from 'swagger-jsdoc';
import swaggerUi from 'swagger-ui-express';

const options = {
  definition: {
    openapi: '3.0.0',
    info: {
      title: 'Trae API',
      version: '1.0.0',
      description: 'API documentation for Trae application',
    },
    servers: [
      {
        url: 'http://localhost:8000/api',
        description: 'Development server',
      },
    ],
    components: {
      securitySchemes: {
        bearerAuth: {
          type: 'http',
          scheme: 'bearer',
          bearerFormat: 'JWT',
        },
      },
    },
  },
  apis: ['./src/routes/*.ts'], // 扫描路由文件中的注释
};

const specs = swaggerJsdoc(options);

export { specs, swaggerUi };

/**
 * @swagger
 * /users:
 *   get:
 *     summary: Get all users
 *     tags: [Users]
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: query
 *         name: page
 *         schema:
 *           type: integer
 *         description: Page number
 *       - in: query
 *         name: limit
 *         schema:
 *           type: integer
 *         description: Number of items per page
 *     responses:
 *       200:
 *         description: List of users
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 users:
 *                   type: array
 *                   items:
 *                     $ref: '#/components/schemas/User'
 *                 pagination:
 *                   $ref: '#/components/schemas/Pagination'
 */

GraphQL API

GraphQL Schema

typescript
// schema/typeDefs.ts
import { gql } from 'apollo-server-express';

export const typeDefs = gql`
  type User {
    id: ID!
    email: String!
    name: String
    avatar: String
    role: Role!
    posts: [Post!]!
    createdAt: String!
    updatedAt: String!
  }

  type Post {
    id: ID!
    title: String!
    content: String
    published: Boolean!
    author: User!
    createdAt: String!
    updatedAt: String!
  }

  enum Role {
    USER
    ADMIN
  }

  type Query {
    users(page: Int, limit: Int): UserConnection!
    user(id: ID!): User
    posts(published: Boolean): [Post!]!
    post(id: ID!): Post
  }

  type Mutation {
    createUser(input: CreateUserInput!): User!
    updateUser(id: ID!, input: UpdateUserInput!): User!
    deleteUser(id: ID!): Boolean!
    createPost(input: CreatePostInput!): Post!
    updatePost(id: ID!, input: UpdatePostInput!): Post!
    deletePost(id: ID!): Boolean!
  }

  input CreateUserInput {
    email: String!
    name: String
    role: Role = USER
  }

  input UpdateUserInput {
    email: String
    name: String
    role: Role
  }

  input CreatePostInput {
    title: String!
    content: String
    published: Boolean = false
  }

  input UpdatePostInput {
    title: String
    content: String
    published: Boolean
  }

  type UserConnection {
    nodes: [User!]!
    pageInfo: PageInfo!
  }

  type PageInfo {
    hasNextPage: Boolean!
    hasPreviousPage: Boolean!
    startCursor: String
    endCursor: String
  }
`;

GraphQL Resolvers

typescript
// schema/resolvers.ts
import { databaseService } from '../services/DatabaseService';
import { AuthenticationError, ForbiddenError } from 'apollo-server-express';

export const resolvers = {
  Query: {
    users: async (_, { page = 1, limit = 10 }, { user }) => {
      if (!user) throw new AuthenticationError('Not authenticated');
      if (user.role !== 'ADMIN') throw new ForbiddenError('Not authorized');

      const skip = (page - 1) * limit;
      const users = await databaseService.client.user.findMany({
        skip,
        take: limit,
        orderBy: { createdAt: 'desc' },
      });

      const total = await databaseService.client.user.count();
      const hasNextPage = skip + limit < total;
      const hasPreviousPage = page > 1;

      return {
        nodes: users,
        pageInfo: {
          hasNextPage,
          hasPreviousPage,
          startCursor: users[0]?.id,
          endCursor: users[users.length - 1]?.id,
        },
      };
    },

    user: async (_, { id }, { user }) => {
      if (!user) throw new AuthenticationError('Not authenticated');
      
      return databaseService.client.user.findUnique({
        where: { id },
        include: { posts: true },
      });
    },

    posts: async (_, { published }, { user }) => {
      if (!user) throw new AuthenticationError('Not authenticated');
      
      return databaseService.client.post.findMany({
        where: published !== undefined ? { published } : {},
        include: { author: true },
        orderBy: { createdAt: 'desc' },
      });
    },
  },

  Mutation: {
    createUser: async (_, { input }, { user }) => {
      if (!user || user.role !== 'ADMIN') {
        throw new ForbiddenError('Not authorized');
      }

      return databaseService.client.user.create({
        data: input,
      });
    },

    createPost: async (_, { input }, { user }) => {
      if (!user) throw new AuthenticationError('Not authenticated');

      return databaseService.client.post.create({
        data: {
          ...input,
          authorId: user.id,
        },
        include: { author: true },
      });
    },
  },

  User: {
    posts: async (parent) => {
      return databaseService.client.post.findMany({
        where: { authorId: parent.id },
      });
    },
  },

  Post: {
    author: async (parent) => {
      return databaseService.client.user.findUnique({
        where: { id: parent.authorId },
      });
    },
  },
};

部署配置

Docker 容器化

Dockerfile

dockerfile
# 多阶段构建
FROM node:18-alpine AS builder

WORKDIR /app

# 复制依赖文件
COPY package*.json ./
COPY prisma ./prisma/

# 安装依赖
RUN npm ci --only=production && npm cache clean --force

# 生成 Prisma 客户端
RUN npx prisma generate

# 复制源代码
COPY . .

# 构建应用
RUN npm run build

# 生产阶段
FROM node:18-alpine AS production

WORKDIR /app

# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

# 复制构建产物
COPY --from=builder --chown=nextjs:nodejs /app/dist ./dist
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nextjs:nodejs /app/package.json ./package.json
COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma

# 切换到非 root 用户
USER nextjs

# 暴露端口
EXPOSE 8000

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:8000/api/health || exit 1

# 启动应用
CMD ["npm", "start"]

docker-compose.yml

yaml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8000:8000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
      - JWT_SECRET=your-jwt-secret
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis
    volumes:
      - ./uploads:/app/uploads
    restart: unless-stopped

  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=myapp
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    ports:
      - "5432:5432"
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - app
    restart: unless-stopped

volumes:
  postgres_data:
  redis_data:

networks:
  default:
    driver: bridge

CI/CD 流水线

GitHub Actions

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

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    
    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: test_db
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run linting
        run: npm run lint
      
      - name: Run type checking
        run: npm run type-check
      
      - name: Run tests
        run: npm run test:coverage
        env:
          DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db
      
      - 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@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build application
        run: npm run build
      
      - name: Build Docker image
        run: |
          docker build -t myapp:${{ github.sha }} .
          docker tag myapp:${{ github.sha }} myapp:latest
      
      - name: Login to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      
      - name: Push Docker image
        run: |
          docker push myapp:${{ github.sha }}
          docker push myapp:latest

  deploy:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
      - name: Deploy to production
        uses: appleboy/ssh-action@v0.1.5
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            cd /opt/myapp
            docker-compose pull
            docker-compose up -d
            docker system prune -f

环境管理

环境配置

环境变量管理

typescript
// config/environment.ts
import { z } from 'zod';

const envSchema = z.object({
  NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
  PORT: z.string().transform(Number).default('8000'),
  DATABASE_URL: z.string().url(),
  JWT_SECRET: z.string().min(32),
  REDIS_URL: z.string().url().optional(),
  AWS_ACCESS_KEY_ID: z.string().optional(),
  AWS_SECRET_ACCESS_KEY: z.string().optional(),
  AWS_REGION: z.string().default('us-east-1'),
  SMTP_HOST: z.string().optional(),
  SMTP_PORT: z.string().transform(Number).optional(),
  SMTP_USER: z.string().optional(),
  SMTP_PASS: z.string().optional(),
});

const env = envSchema.parse(process.env);

export const config = {
  app: {
    env: env.NODE_ENV,
    port: env.PORT,
    isDevelopment: env.NODE_ENV === 'development',
    isProduction: env.NODE_ENV === 'production',
    isTest: env.NODE_ENV === 'test',
  },
  database: {
    url: env.DATABASE_URL,
  },
  auth: {
    jwtSecret: env.JWT_SECRET,
    jwtExpiresIn: '7d',
  },
  redis: {
    url: env.REDIS_URL,
  },
  aws: {
    accessKeyId: env.AWS_ACCESS_KEY_ID,
    secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
    region: env.AWS_REGION,
  },
  email: {
    host: env.SMTP_HOST,
    port: env.SMTP_PORT,
    user: env.SMTP_USER,
    pass: env.SMTP_PASS,
  },
};

多环境配置

bash
# .env.development
NODE_ENV=development
PORT=8000
DATABASE_URL=postgresql://postgres:password@localhost:5432/myapp_dev
JWT_SECRET=development-jwt-secret-key-32-chars
REDIS_URL=redis://localhost:6379
LOG_LEVEL=debug

# .env.production
NODE_ENV=production
PORT=8000
DATABASE_URL=${DATABASE_URL}
JWT_SECRET=${JWT_SECRET}
REDIS_URL=${REDIS_URL}
LOG_LEVEL=info

# .env.test
NODE_ENV=test
PORT=8001
DATABASE_URL=postgresql://postgres:password@localhost:5432/myapp_test
JWT_SECRET=test-jwt-secret-key-32-characters
LOG_LEVEL=silent

代码质量

代码规范

ESLint 配置

javascript
// .eslintrc.js
module.exports = {
  extends: [
    'eslint:recommended',
    '@typescript-eslint/recommended',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended',
    'plugin:jsx-a11y/recommended',
    'prettier'
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaVersion: 2022,
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true
    }
  },
  plugins: [
    '@typescript-eslint',
    'react',
    'react-hooks',
    'jsx-a11y',
    'import'
  ],
  rules: {
    'react/react-in-jsx-scope': 'off',
    'react/prop-types': 'off',
    '@typescript-eslint/no-unused-vars': 'error',
    '@typescript-eslint/no-explicit-any': 'warn',
    'import/order': [
      'error',
      {
        groups: [
          'builtin',
          'external',
          'internal',
          'parent',
          'sibling',
          'index'
        ],
        'newlines-between': 'always'
      }
    ],
    'prefer-const': 'error',
    'no-var': 'error',
    'no-console': 'warn'
  },
  settings: {
    react: {
      version: 'detect'
    }
  }
};

Prettier 配置

json
{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "printWidth": 80,
  "tabWidth": 2,
  "useTabs": false,
  "bracketSpacing": true,
  "bracketSameLine": false,
  "arrowParens": "avoid",
  "endOfLine": "lf"
}

Git Hooks

Husky 配置

bash
# .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged

# .husky/commit-msg
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx commitlint --edit $1

# .husky/pre-push
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run type-check
npm run test

Commitlint 配置

javascript
// commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      [
        'feat',     // 新功能
        'fix',      // 修复
        'docs',     // 文档
        'style',    // 格式
        'refactor', // 重构
        'perf',     // 性能优化
        'test',     // 测试
        'chore',    // 构建过程或辅助工具的变动
        'ci',       // CI 配置
        'build',    // 构建系统
        'revert'    // 回滚
      ]
    ],
    'subject-max-length': [2, 'always', 100],
    'subject-case': [2, 'never', ['pascal-case', 'upper-case']]
  }
};

监控和分析

性能监控

应用性能监控

typescript
// monitoring/performance.ts
import { performance } from 'perf_hooks';
import { Request, Response, NextFunction } from 'express';

interface PerformanceMetrics {
  endpoint: string;
  method: string;
  duration: number;
  statusCode: number;
  timestamp: Date;
}

class PerformanceMonitor {
  private metrics: PerformanceMetrics[] = [];

  middleware() {
    return (req: Request, res: Response, next: NextFunction) => {
      const startTime = performance.now();
      
      res.on('finish', () => {
        const endTime = performance.now();
        const duration = endTime - startTime;
        
        const metric: PerformanceMetrics = {
          endpoint: req.route?.path || req.path,
          method: req.method,
          duration,
          statusCode: res.statusCode,
          timestamp: new Date()
        };
        
        this.recordMetric(metric);
    
    // 发送到监控服务
    this.sendToMonitoring(metric);
  }

  private sendToMonitoring(metric: PerformanceMetrics) {
    // 发送到 Prometheus、DataDog 等监控服务
    if (process.env.MONITORING_ENABLED === 'true') {
      // 实现监控数据发送逻辑
    }
  }

  getMetrics() {
    return this.metrics;
  }

  getAverageResponseTime(endpoint?: string) {
    const filteredMetrics = endpoint 
      ? this.metrics.filter(m => m.endpoint === endpoint)
      : this.metrics;
    
    if (filteredMetrics.length === 0) return 0;
    
    const total = filteredMetrics.reduce((sum, m) => sum + m.duration, 0);
    return total / filteredMetrics.length;
  }
}

export const performanceMonitor = new PerformanceMonitor();

错误监控

typescript
// monitoring/errorTracking.ts
import { Request, Response, NextFunction } from 'express';

interface ErrorLog {
  message: string;
  stack?: string;
  endpoint: string;
  method: string;
  userAgent?: string;
  ip: string;
  timestamp: Date;
  userId?: string;
}

class ErrorTracker {
  private errors: ErrorLog[] = [];

  middleware() {
    return (error: Error, req: Request, res: Response, next: NextFunction) => {
      const errorLog: ErrorLog = {
        message: error.message,
        stack: error.stack,
        endpoint: req.path,
        method: req.method,
        userAgent: req.get('User-Agent'),
        ip: req.ip,
        timestamp: new Date(),
        userId: req.user?.id
      };

      this.logError(errorLog);
      
      // 发送到错误追踪服务(如 Sentry)
      this.sendToErrorService(error, req);
      
      res.status(500).json({
        error: 'Internal Server Error',
        message: process.env.NODE_ENV === 'development' ? error.message : 'Something went wrong'
      });
    };
  }

  private logError(errorLog: ErrorLog) {
    this.errors.push(errorLog);
    console.error('Error logged:', errorLog);
  }

  private sendToErrorService(error: Error, req: Request) {
    // 集成 Sentry 或其他错误追踪服务
    if (process.env.SENTRY_DSN) {
      // Sentry.captureException(error, { req });
    }
  }
}

export const errorTracker = new ErrorTracker();

日志管理

结构化日志

typescript
// utils/logger.ts
import winston from 'winston';
import { config } from '../config/environment';

const logFormat = winston.format.combine(
  winston.format.timestamp(),
  winston.format.errors({ stack: true }),
  winston.format.json()
);

const logger = winston.createLogger({
  level: config.app.isDevelopment ? 'debug' : 'info',
  format: logFormat,
  defaultMeta: { service: 'trae-api' },
  transports: [
    new winston.transports.File({ 
      filename: 'logs/error.log', 
      level: 'error' 
    }),
    new winston.transports.File({ 
      filename: 'logs/combined.log' 
    })
  ]
});

if (config.app.isDevelopment) {
  logger.add(new winston.transports.Console({
    format: winston.format.combine(
      winston.format.colorize(),
      winston.format.simple()
    )
  }));
}

export { logger };

团队协作

代码审查流程

Pull Request 模板

markdown
<!-- .github/pull_request_template.md -->
## 变更描述

### 变更类型
- [ ] 新功能 (feature)
- [ ] 修复 (fix)
- [ ] 文档 (docs)
- [ ] 样式 (style)
- [ ] 重构 (refactor)
- [ ] 性能优化 (perf)
- [ ] 测试 (test)
- [ ] 构建 (build)
- [ ] CI (ci)
- [ ] 其他 (chore)

### 变更内容
- 
- 
- 

### 测试
- [ ] 单元测试已通过
- [ ] 集成测试已通过
- [ ] 手动测试已完成
- [ ] 性能测试已完成(如适用)

### 检查清单
- [ ] 代码遵循项目规范
- [ ] 已添加必要的测试
- [ ] 文档已更新
- [ ] 无破坏性变更
- [ ] 已考虑向后兼容性

### 截图(如适用)

### 相关 Issue
Closes #

### 额外说明

代码审查指南

markdown
# 代码审查指南

## 审查重点

### 功能性
- [ ] 代码是否实现了预期功能
- [ ] 边界条件是否处理正确
- [ ] 错误处理是否完善
- [ ] 性能是否满足要求

### 代码质量
- [ ] 代码结构是否清晰
- [ ] 命名是否有意义
- [ ] 是否遵循 DRY 原则
- [ ] 是否有代码重复

### 安全性
- [ ] 是否存在安全漏洞
- [ ] 输入验证是否充分
- [ ] 敏感信息是否正确处理
- [ ] 权限控制是否合理

### 测试
- [ ] 测试覆盖率是否足够
- [ ] 测试用例是否全面
- [ ] 测试是否可靠

### 文档
- [ ] 代码注释是否清晰
- [ ] API 文档是否更新
- [ ] README 是否需要更新

项目管理

Issue 模板

markdown
<!-- .github/ISSUE_TEMPLATE/bug_report.md -->
---
name: Bug 报告
about: 创建一个 bug 报告来帮助我们改进
title: '[BUG] '
labels: 'bug'
assignees: ''
---

## Bug 描述
简洁明了地描述这个 bug。

## 复现步骤
1. 进入 '...'
2. 点击 '....'
3. 滚动到 '....'
4. 看到错误

## 期望行为
简洁明了地描述你期望发生什么。

## 实际行为
简洁明了地描述实际发生了什么。

## 截图
如果适用,添加截图来帮助解释你的问题。

## 环境信息
- OS: [e.g. macOS, Windows, Linux]
- 浏览器: [e.g. Chrome, Safari, Firefox]
- 版本: [e.g. 22]
- Node.js 版本: [e.g. 18.0.0]

## 额外信息
在这里添加关于问题的任何其他信息。

最佳实践

开发规范

文件组织

src/
├── components/          # 可复用组件
│   ├── ui/             # 基础 UI 组件
│   ├── forms/          # 表单组件
│   └── layout/         # 布局组件
├── pages/              # 页面组件
├── hooks/              # 自定义 Hooks
├── services/           # API 服务
├── utils/              # 工具函数
├── types/              # TypeScript 类型定义
├── constants/          # 常量定义
├── styles/             # 样式文件
└── tests/              # 测试文件

命名规范

typescript
// 组件命名:PascalCase
const UserProfile = () => {};
const NavigationBar = () => {};

// 函数命名:camelCase
const getUserData = () => {};
const handleSubmit = () => {};

// 常量命名:SCREAMING_SNAKE_CASE
const API_BASE_URL = 'https://api.example.com';
const MAX_RETRY_ATTEMPTS = 3;

// 文件命名:kebab-case
// user-profile.component.tsx
// api-service.ts
// user-types.ts

代码注释

typescript
/**
 * 用户服务类
 * 处理所有与用户相关的 API 操作
 */
class UserService {
  /**
   * 获取用户信息
   * @param userId - 用户 ID
   * @returns Promise<User> 用户信息
   * @throws {NotFoundError} 当用户不存在时
   */
  async getUser(userId: string): Promise<User> {
    // 实现逻辑
  }

  /**
   * 更新用户信息
   * @param userId - 用户 ID
   * @param updateData - 要更新的数据
   * @returns Promise<User> 更新后的用户信息
   */
  async updateUser(userId: string, updateData: Partial<User>): Promise<User> {
    // 实现逻辑
  }
}

性能优化

React 性能优化

typescript
// 使用 React.memo 优化组件渲染
const UserCard = React.memo(({ user, onEdit }: UserCardProps) => {
  return (
    <div className="user-card">
      <img src={user.avatar} alt={user.name} />
      <h3>{user.name}</h3>
      <button onClick={() => onEdit(user.id)}>编辑</button>
    </div>
  );
});

// 使用 useMemo 优化计算
const ExpensiveComponent = ({ items }: { items: Item[] }) => {
  const expensiveValue = useMemo(() => {
    return items.reduce((sum, item) => sum + item.value, 0);
  }, [items]);

  return <div>总计: {expensiveValue}</div>;
};

// 使用 useCallback 优化函数引用
const ParentComponent = () => {
  const [count, setCount] = useState(0);
  
  const handleIncrement = useCallback(() => {
    setCount(prev => prev + 1);
  }, []);

  return <ChildComponent onIncrement={handleIncrement} />;
};

代码分割和懒加载

typescript
// 路由级代码分割
const Dashboard = lazy(() => import('./pages/Dashboard'));
const UserManagement = lazy(() => import('./pages/UserManagement'));
const Settings = lazy(() => import('./pages/Settings'));

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

const App = () => {
  return (
    <Router>
      <Suspense fallback={<LoadingSpinner />}>
        <Routes>
          <Route path="/dashboard" element={<Dashboard />} />
          <Route path="/users" element={<UserManagement />} />
          <Route path="/settings" element={<Settings />} />
        </Routes>
      </Suspense>
    </Router>
  );
};

入门指南

快速开始

  1. 创建新项目
bash
trae create my-project --template react-typescript
cd my-project
  1. 启动开发服务器
bash
npm run dev
  1. 开始开发
  • src/ 目录下编写代码
  • 使用 AI 助手生成组件和功能
  • 实时预览更改
  1. 构建和部署
bash
npm run build
npm run deploy

学习资源

通过 Trae IDE 的项目开发功能,你可以高效地构建现代化的 Web 应用程序,从项目初始化到生产部署,每个环节都有完善的工具支持。

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