dpsk

19 阅读3分钟

目录

  1. 核心原则
  2. 组件目录结构
  3. 代码分层实现
    • UI 组件 (Dumb Component)
    • 状态管理 (Hooks/Context)
    • 操作逻辑 (Services/Actions)
  4. 完整示例
  5. 注意事项

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. 注意事项

  1. 服务端组件优化(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} />;  
    }  
    
  2. 测试策略

    • UI 组件:使用 Jest + Testing Library 测试渲染逻辑。
    • Hooks:使用 @testing-library/react-hooks 测试状态变化。
    • Services:Mock API 请求,验证数据转换和 Zod 校验。
  3. 性能优化

    • 使用 React.memouseMemo 避免不必要的渲染。
    • 对高频操作(如搜索)添加防抖(Debounce)。
  4. 错误边界

    • 使用 ErrorBoundary 组件捕获并展示全局错误。

通过这种分层设计,你的 Next.js 应用将具备以下优势:

  • UI 与逻辑解耦:设计师可独立修改 UI,开发者专注业务逻辑。
  • 代码复用性:Service 和 Hook 可在多个组件中复用。
  • 易于测试:各层可独立测试,无需模拟复杂依赖。
  • 维护性提升:问题定位更快速,代码变更影响范围可控。