8.7 KiB
8.7 KiB
最佳实践
> 文档版本: v1.0.0 > 创建日期: 2026-05-25 > 适用范围: FischerX 项目开发人员
目录
前端最佳实践
组件设计
单一职责原则
每个组件应该只做一件事,保持组件保持组件应该组件应该只做一件事
// 好的做法
function UserList({ users }: { users: User[] }) {
return (
<div className="user-list">
{users.map(user => (
<UserCard key={user.id} user={user} />
))}
</div>
);
}
function UserCard({ user }: { user: User }) {
return (
<div className="user-card">
<UserAvatar user={user} />
<UserInfo user={user} />
</div>
);
}
// 避免 - 组件太大太组件太组件太大了
function UserPage({ users }: { users: User[] }) {
return (
<div className="user-page">
{users.map(user => (
<div key={user.id} className="user-card">
<img src={user.avatar} alt={user.name} />
<div>
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
</div>
))}
</div>
);
}
Props 解构与组合
使用组合而不是继承,使用组合而不是继承
// 好的做法
function Button({
children, variant = 'primary', className, ...props }: ButtonProps) {
return (
<button
className={cn(buttonVariants({ variant }), className)}
{...props}
>
{children}
</button>
);
}
// 使用
<Button variant="secondary" onClick={handleClick} disabled={isLoading}>
<Icon />
Submit
</Button>
状态管理
使用 React Query 进行服务器状态
// hooks/useUsers.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { userApi } from '@/lib/api';
export function useUsers() {
return useQuery({
queryKey: ['users'],
queryFn: userApi.getAll,
});
}
export function useCreateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: userApi.create,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
}
使用 Zustand 进行客户端状态
// stores/useAuthStore.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface AuthState {
user: User | null;
token: string | null;
setUser: (user: User | null) => void;
setToken: (token: string | null) => void;
logout: () => void;
}
export const useAuthStore = create<AuthState>()(
persist(
(set) => ({
user: null,
token: null,
setUser: (user) => set({ user }),
setToken: (token) => set({ token }),
logout: () => set({ user: null, token: null }),
}),
{
name: 'auth-storage',
}
)
);
性能优化
组件懒加载
// 使用动态导入
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
loading: () => <div>Loading...</div>,
ssr: false,
});
// 在需要时再加载
function SomePage() {
const [showHeavy, setShowHeavy] = useState(false);
return (
<div>
<button onClick={() => setShowHeavy(true)}>Show</button>
{showHeavy && <HeavyComponent />}
</div>
);
}
使用 useMemo 和 useCallback
// 只在 dependencies 变化时重新计算
const filteredUsers = useMemo(() => {
return users.filter(user => user.active);
}, [users]);
// 只在 dependencies 变化时重新创建函数
const handleUserClick = useCallback((user: User) => {
navigate(`/users/${user.id}`);
}, [navigate]);
后端最佳实践
API 设计
RESTful 规范
// 好的做法
GET /users # 获取用户列表
GET /users/:id # 获取单个用户
POST /users # 创建用户
PUT /users/:id # 更新用户
DELETE /users/:id # 删除用户
GET /users/:id/orders # 获取用户订单
统一响应格式
// 成功响应
{
"success": true,
"data": { ... },
"message": "Operation successful"
}
// 错误响应
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "User not found",
"details": []
}
}
数据库设计
使用 Prisma 迁移
// schema.prisma
model User {
id String @id @default(cuid())
name String
email String @unique
password String
role Role @default(USER)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([email])
}
enum Role {
USER
ADMIN
}
数据库查询优化
// 使用 select 只查询需要的字段
async function getUsers() {
return this.prisma.user.findMany({
select: {
id: true,
name: true,
email: true,
},
});
}
// 使用 include 加载关联数据
async function getUserWithOrders(id: string) {
return this.prisma.user.findUnique({
where: { id },
include: { orders: true },
});
}
缓存策略
// 使用 Redis 缓存
@Injectable()
export class UserService {
constructor(
private prisma: PrismaService,
@Inject(CACHE_MANAGER) private cacheManager: Cache,
) {}
async findOne(id: string) {
const cacheKey = `user:${id}`;
const cached = await this.cacheManager.get(cacheKey);
if (cached) {
return cached;
}
const user = await this.prisma.user.findUnique({ where: { id } });
await this.cacheManager.set(cacheKey, user, 300); // 5 分钟
return user;
}
}
共享包开发
包的设计原则
高内聚低耦合
// packages/utils/src/string.ts
export function formatDate(date: Date): string {
return new Intl.DateTimeFormat('zh-CN').format(date);
}
export function truncate(str: string, length: number): string {
return str.length > length ? `${str.slice(0, length)}...` : str;
}
export function capitalize(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1);
}
明确的导出
// packages/utils/src/index.ts
export * from './string';
export * from './date';
export * from './validation';
类型安全
// packages/types/src/user.ts
export interface User {
id: string;
name: string;
email: string;
role: 'admin' | 'user';
}
export type CreateUserInput = Omit<User, 'id'>;
export type UpdateUserInput = Partial<CreateUserInput>;
测试最佳实践
单元测试
使用 Vitest 进行前端测试
// components/__tests__/UserCard.test.tsx
import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import { UserCard } from '../UserCard';
describe('UserCard', () => {
const user = {
id: '1',
name: 'John Doe',
email: 'john@example.com',
};
it('renders user information', () => {
render(<UserCard user={user} />);
expect(screen.getByText('John Doe')).toBeInTheDocument();
expect(screen.getByText('john@example.com')).toBeInTheDocument();
});
});
使用 Jest 进行后端测试
// services/api/src/user/user.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { UserService } from './user.service';
describe('UserService', () => {
let service: UserService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [UserService],
}).compile();
service = module.get<UserService>(UserService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
测试原则
- AAA 模式: Arrange, Act, Assert
- **描述性的测试名称
- **测试应该独立
- **测试行为而不是实现
describe('UserService', () => {
it('should create a new user', async () => {
// Arrange
const createUserDto = {
name: 'Test',
email: 'test@example.com',
password: 'password123',
};
// Act
const result = await service.create(createUserDto);
// Assert
expect(result.name).toEqual('Test');
});
});
下一步
> 文档维护: 本文档由开发团队维护,如有问题或建议请提交 Issue。 > 最后更新: 2026-05-25