元宝

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 接口确保类型安全。

相关推荐
崔庆才丨静觅11 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606112 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了12 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅12 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅12 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅13 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment13 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅13 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊13 小时前
jwt介绍
前端
爱敲代码的小鱼13 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax