You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

6.1 KiB

开发规范

文档版本:v1.1
最后更新:2025-01-18


目录

  1. 文件命名规范
  2. 组件编写规范
  3. 导入顺序规范
  4. Git Commit 规范

1. 文件命名规范

类型 命名规范 示例
组件文件 PascalCase StoryboardItem.tsx
Hook 文件 camelCase useStoryboards.ts
工具文件 camelCase formatDuration.ts
类型文件 camelCase storyboard.ts
常量文件 camelCase routes.ts
样式文件 kebab-case globals.css
测试文件 原文件名.test StoryboardItem.test.tsx

2. 组件编写规范

2.1 组件文件结构

// 组件文件结构
import { useState, useCallback, memo } from 'react';
import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';
import type { Storyboard } from '@types';

// Props 类型定义
interface StoryboardItemProps {
  storyboard: Storyboard;
  isSelected: boolean;
  onSelect: (id: string) => void;
  className?: string;
}

// 组件实现
export const StoryboardItem = memo(function StoryboardItem({
  storyboard,
  isSelected,
  onSelect,
  className,
}: StoryboardItemProps) {
  // Hooks
  const [isHovered, setIsHovered] = useState(false);

  // 事件处理
  const handleClick = useCallback(() => {
    onSelect(storyboard.id);
  }, [storyboard.id, onSelect]);

  // 渲染
  return (
    <div
      className={cn(
        'p-3 rounded-md cursor-pointer transition-colors',
        isSelected && 'bg-primary/10 border-l-2 border-primary',
        isHovered && 'bg-muted',
        className
      )}
      onClick={handleClick}
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
    >
      <h3 className="font-medium">{storyboard.title}</h3>
      <p className="text-sm text-muted-foreground">{storyboard.description}</p>
    </div>
  );
});

2.2 组件编写原则

  1. 单一职责:每个组件只负责一个功能
  2. Props 类型化:所有 Props 必须定义类型
  3. 使用 memo:对于频繁渲染的组件使用 memo 优化
  4. 事件处理:使用 useCallback 缓存事件处理函数
  5. 样式管理:使用 cn 工具函数合并样式

3. 导入顺序规范

// 1. React 相关
import { useState, useEffect, useCallback } from "react";

// 2. 第三方库
import { useQuery } from "@tanstack/react-query";
import { z } from "zod";

// 3. 内部别名导入 - 组件
import { Button } from "@components/ui/button";
import { LoadingSpinner } from "@components/common";

// 4. 内部别名导入 - 其他
import { useStoryboards } from "@hooks/api/useStoryboards";
import { useEditorStore } from "@stores/editorStore";
import { cn } from "@/lib/utils";

// 5. 类型导入
import type { Storyboard } from "@types";

// 6. 相对路径导入
import { StoryboardItem } from "./StoryboardItem";
import "./StoryboardPanel.css";

4. Git Commit 规范

4.1 Commit 消息格式

<type>(<scope>): <subject>

<body>

<footer>

4.2 类型说明

类型 说明 示例
feat 新功能 feat(storyboard): 添加分镜拖拽排序
fix 修复 fix(timeline): 修复分镜看板缩放问题
docs 文档 docs(readme): 更新安装说明
style 格式 style(button): 调整按钮间距
refactor 重构 refactor(api): 重构 API 客户端
perf 性能 perf(list): 使用虚拟列表优化
test 测试 test(hooks): 添加 useDebounce 测试
chore 构建/工具 chore(deps): 升级依赖版本

4.3 Commit 示例

# 好的示例
feat(storyboard): 添加分镜拖拽排序功能

- 使用 dnd-kit 实现拖拽
- 支持跨分镜组拖拽
- 添加拖拽动画效果

Closes #123

# 不好的示例
update code
fix bug

5. 代码风格

5.1 TypeScript 规范

// ✅ 推荐:使用接口定义 Props
interface ButtonProps {
  variant?: "primary" | "secondary";
  size?: "sm" | "md" | "lg";
  onClick?: () => void;
}

// ❌ 避免:使用 any
function handleData(data: any) {}

// ✅ 推荐:明确类型
function handleData(data: User) {}

// ✅ 推荐:使用可选链
const name = user?.profile?.name;

// ❌ 避免:多层判断
const name = user && user.profile && user.profile.name;

5.2 React 规范

// ✅ 推荐:使用函数组件
function Button({ children }: ButtonProps) {
  return <button>{children}</button>;
}

// ❌ 避免:使用类组件(除非必要)
class Button extends React.Component { }

// ✅ 推荐:使用 Hooks
const [count, setCount] = useState(0);

// ✅ 推荐:提取自定义 Hook
function useCounter(initialValue: number) {
  const [count, setCount] = useState(initialValue);
  const increment = () => setCount(c => c + 1);
  return { count, increment };
}

6. 注释规范

6.1 组件注释

/**
 * 分镜项组件
 *
 * @param storyboard - 分镜数据
 * @param isSelected - 是否选中
 * @param onSelect - 选中回调
 */
export function StoryboardItem({
  storyboard,
  isSelected,
  onSelect,
}: StoryboardItemProps) {
  // ...
}

6.2 函数注释

/**
 * 格式化时长
 *
 * @param seconds - 秒数
 * @returns 格式化后的时长字符串 (mm:ss 或 hh:mm:ss)
 *
 * @example
 * formatDuration(65) // "01:05"
 * formatDuration(3661) // "01:01:01"
 */
export function formatDuration(seconds: number): string {
  // ...
}

相关文档


最后更新:2025-01-18 | 版本:v1.1