元宝

Next.js 组件开发最佳实践文档

(基于模块化、TypeScript、UI/状态/操作分离设计)


目录结构规范

bash 复制代码
components/
├─ search/                  # 功能模块目录(如搜索相关组件)
│  ├─ hooks/               # 模块专属自定义 Hook
│  │  └─ use-search.ts     
│  ├─ agent-card/          # 单个组件独立目录
│  │  ├─ agent-card.tsx    # 主组件(UI + 接口定义)
│  │  ├─ agent-card.stories.tsx  # Storybook 交互文档
│  │  ├─ agent-card.test.tsx     # 单元测试
│  │  └─ index.ts          # 统一导出
│  └─ ...                  
├─ ui/                     # 基础 UI 组件库
│  ├─ prompt-input/        # 带封装逻辑的 UI 组件
│  │  ├─ prompt-input.tsx  
│  │  ├─ prompt-input.stories.tsx
│  │  └─ index.ts
│  └─ ...
└─ ...

组件设计原则

1. 类型定义先行(TypeScript First)

typescript 复制代码
// components/search/agent-card/agent-card.tsx
interface AgentData {
  id: string;
  name: string;
  status: 'online' | 'offline';
}

type AgentCardProps = {
  agent: AgentData;
  onSelect?: (id: string) => void;
  className?: string;
};

2. UI 与逻辑分离

tsx 复制代码
// components/search/agent-card/agent-card.tsx
export const AgentCardUI = ({ 
  name, 
  status, 
  onSelect 
}: Pick<AgentCardProps, 'name' | 'status' | 'onSelect'>) => {
  return (
    <div className={styles.card} onClick={() => onSelect?.()}>
      <Avatar name={name} />
      <StatusBadge status={status} />
    </div>
  );
};

export const AgentCard = ({ agent, ...props }: AgentCardProps) => {
  // 逻辑处理层(数据转换/状态管理)
  const { isHighlighted } = useAgentHighlights(agent.id);
  
  return (
    <AgentCardUI 
      {...agent} 
      {...props}
      className={clsx(isHighlighted && styles.highlight)}
    />
  );
};

3. 状态管理策略

组件状态 :使用 useStateuseReducer 管理局部状态

跨组件状态:通过 Context + Custom Hook 封装

typescript 复制代码
// components/search/hooks/use-search.ts
export const useSearch = () => {
  const [keywords, setKeywords] = useState('');
  const { data, isLoading } = useSWR(`/api/search?q=${keywords}`, fetcher);

  return {
    keywords,
    setKeywords,
    results: data,
    isLoading
  };
};

4. 操作行为封装

• 事件处理函数独立为纯函数

• 异步操作使用 Service 层抽象

typescript 复制代码
// components/search/agent-card/actions.ts
export const fetchAgentDetails = async (id: string) => {
  const response = await fetch(`/api/agents/${id}`);
  return response.json();
};

// 在组件中使用
const handleSelect = async (id: string) => {
  const details = await fetchAgentDetails(id);
  // ...更新状态
};

文档与测试规范

1. Storybook 交互文档

typescript 复制代码
// components/search/agent-card/agent-card.stories.tsx
const meta = {
  title: 'Search/AgentCard',
  component: AgentCard,
  tags: ['autodocs'],
} satisfies Meta<typeof AgentCard>;

export default meta;

export const OnlineAgent = {
  args: {
    agent: {
      id: '1',
      name: 'John Doe',
      status: 'online'
    }
  }
};

export const OfflineAgent = {
  args: {
    ...OnlineAgent.args,
    agent: { ...OnlineAgent.args.agent, status: 'offline' }
  }
};

2. 单元测试规范

typescript 复制代码
// components/search/agent-card/agent-card.test.tsx
describe('AgentCard', () => {
  it('显示在线状态徽章', () => {
    const { getByText } = render(
      <AgentCard agent={{ id: '1', name: 'Test', status: 'online' }} />
    );
    expect(getByText('online')).toBeInTheDocument();
  });

  it('点击触发选择事件', async () => {
    const mockSelect = vi.fn();
    const { container } = render(
      <AgentCard agent={mockAgent} onSelect={mockSelect} />
    );
    await user.click(container.firstChild!);
    expect(mockSelect).toHaveBeenCalledWith('1');
  });
});

代码组织最佳实践

1. 模块化入口文件

typescript 复制代码
// components/search/agent-card/index.ts
export { AgentCard } from './agent-card';
export type { AgentData, AgentCardProps } from './agent-card';

2. 样式隔离方案

• 使用 CSS Modules 或 Styled Components

• 避免全局样式污染

scss 复制代码
// components/search/agent-card/agent-card.module.scss
.card {
  border: 1px solid var(--neutral-200);
  
  &.highlight {
    border-color: var(--blue-500);
  }
}

3. 组件 Props 设计规范

• 必填属性不加 ? 修饰符

• 事件处理器统一以 on 前缀命名

• 样式扩展使用 className + clsx 组合


总结:核心优势

维度 实现方案
可维护性 模块化目录结构 + 单一职责组件
可测试性 Storybook 可视化 + Jest 单元测试
扩展性 接口驱动设计 + 组合式 Props
类型安全 严格 TypeScript 类型定义
协作效率 自描述组件 + 交互式文档

实施参考 :根据图中 agent-card.tsx 结构,将 UI 渲染与业务逻辑解耦,通过 Storybook 实现可视化调试,结合 TypeScript 接口确保类型安全。

相关推荐
aiprtem19 分钟前
基于Flutter的web登录设计
前端·flutter
浪裡遊22 分钟前
React Hooks全面解析:从基础到高级的实用指南
开发语言·前端·javascript·react.js·node.js·ecmascript·php
why技术29 分钟前
Stack Overflow,轰然倒下!
前端·人工智能·后端
GISer_Jing34 分钟前
0704-0706上海,又聚上了
前端·新浪微博
止观止1 小时前
深入探索 pnpm:高效磁盘利用与灵活的包管理解决方案
前端·pnpm·前端工程化·包管理器
whale fall1 小时前
npm install安装的node_modules是什么
前端·npm·node.js
烛阴1 小时前
简单入门Python装饰器
前端·python
袁煦丞2 小时前
数据库设计神器DrawDB:cpolar内网穿透实验室第595个成功挑战
前端·程序员·远程工作
天天扭码2 小时前
从图片到语音:我是如何用两大模型API打造沉浸式英语学习工具的
前端·人工智能·github
鱼樱前端3 小时前
今天介绍下最新更新的Vite7
前端·vue.js