目录
- 核心原则
- 组件目录结构
- 代码分层实现
- UI 组件 (Dumb Component)
- 状态管理 (Hooks/Context)
- 操作逻辑 (Services/Actions)
- 完整示例
- 注意事项
1. 核心原则
- 单一职责:每个模块/文件只负责一个功能。
- 无副作用 UI:UI 组件仅负责渲染,不处理业务逻辑。
- 状态与 UI 解耦:通过自定义 Hook 或 Context 管理状态。
- 操作复用性:将 API 调用、数据处理等操作抽象为独立服务。
2. 组件目录结构
src/
├─ components/
│ ├─ UserList/ # 组件根目录
│ │ ├─ UserList.tsx # UI 组件
│ │ ├─ useUserList.ts # 状态管理 Hook
│ │ ├─ userService.ts # API 操作封装
│ │ ├─ types.ts # 类型定义
│ │ └─ index.ts # 统一导出
├─ contexts/ # 全局状态(可选)
└─ services/ # 全局 API 服务(可选)
3. 代码分层实现
3.1 UI 组件 (UserList.tsx
)
- 职责:纯渲染,无状态/逻辑。
- 输入:通过
props
接收数据和回调函数。 - 技术要点:
- 使用
React.memo
优化性能。 - 禁止直接调用 API 或操作状态。
- 使用
// components/UserList/UserList.tsx
import React from 'react';
import { User } from './types';
type Props = {
users: User[];
isLoading: boolean;
onDelete: (userId: string) => void;
};
const UserList: React.FC<Props> = React.memo(({ users, isLoading, onDelete }) => {
if (isLoading) return <div>Loading...</div>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>
{user.name}
<button onClick={() => onDelete(user.id)}>Delete</button>
</li>
))}
</ul>
);
});
export default UserList;
3.2 状态管理 (useUserList.ts
)
- 职责:管理组件状态、组合操作逻辑。
- 技术要点:
- 使用
useState
/useEffect
或全局状态库(如 Zustand)。 - 调用 Service 层操作,传递回调给 UI。
- 使用
// components/UserList/useUserList.ts
import { useState, useEffect } from 'react';
import { User } from './types';
import { fetchUsers, deleteUser } from './userService';
const useUserList = () => {
const [users, setUsers] = useState<User[]>([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const loadUsers = async () => {
try {
const data = await fetchUsers();
setUsers(data);
} catch (error) {
console.error('Failed to load users:', error);
} finally {
setIsLoading(false);
}
};
loadUsers();
}, []);
const handleDelete = async (userId: string) => {
try {
await deleteUser(userId);
setUsers(users.filter(user => user.id !== userId));
} catch (error) {
console.error('Delete failed:', error);
}
};
return { users, isLoading, handleDelete };
};
export default useUserList;
3.3 操作逻辑 (userService.ts
)
- 职责:封装 API 请求、数据转换。
- 技术要点:
- 使用 Zod 验证 API 响应数据格式。
- 集中处理错误和日志。
// components/UserList/userService.ts
import { z } from 'zod';
import { User } from './types';
const UserSchema = z.object({
id: z.string(),
name: z.string(),
});
// 模拟 API 调用
export const fetchUsers = async (): Promise<User[]> => {
const response = await fetch('/api/users');
const data = await response.json();
return z.array(UserSchema).parse(data); // Zod 验证
};
export const deleteUser = async (userId: string): Promise<void> => {
await fetch(`/api/users/${userId}`, { method: 'DELETE' });
};
3.4 类型定义 (types.ts
)
// components/UserList/types.ts
export type User = {
id: string;
name: string;
};
4. 完整组件集成
// components/UserList/index.tsx
import UserList from './UserList';
import useUserList from './useUserList';
const UserListContainer = () => {
const { users, isLoading, handleDelete } = useUserList();
return (
<UserList
users={users}
isLoading={isLoading}
onDelete={handleDelete}
/>
);
};
export default UserListContainer;
5. 注意事项
-
服务端组件优化(Next.js App Router):
- 在 Server Component 中获取初始数据,通过
props
传递给客户端组件。
// app/page.tsx import UserListContainer from '@/components/UserList'; import { fetchUsers } from '@/components/UserList/userService'; export default async function Home() { const initialUsers = await fetchUsers(); return <UserListContainer initialUsers={initialUsers} />; }
- 在 Server Component 中获取初始数据,通过
-
测试策略:
- UI 组件:使用 Jest + Testing Library 测试渲染逻辑。
- Hooks:使用
@testing-library/react-hooks
测试状态变化。 - Services:Mock API 请求,验证数据转换和 Zod 校验。
-
性能优化:
- 使用
React.memo
或useMemo
避免不必要的渲染。 - 对高频操作(如搜索)添加防抖(Debounce)。
- 使用
-
错误边界:
- 使用
ErrorBoundary
组件捕获并展示全局错误。
- 使用
通过这种分层设计,你的 Next.js 应用将具备以下优势:
- UI 与逻辑解耦:设计师可独立修改 UI,开发者专注业务逻辑。
- 代码复用性:Service 和 Hook 可在多个组件中复用。
- 易于测试:各层可独立测试,无需模拟复杂依赖。
- 维护性提升:问题定位更快速,代码变更影响范围可控。