Files
cunkebao_v3/Touchkebao/React代码编写规范.md

16 KiB
Raw Blame History

React 代码编写规范大全

参考来源:React 代码编写规范大全:从基础到架构,打造可维护的企业级应用

编写 React 代码不仅仅是让功能跑起来,更重要的是保证代码的可读性、可维护性和团队协作效率。一套良好的编码规范是达成这些目标的基石。

本文将从基础规范组件设计状态管理样式处理性能优化项目架构六个层次,由浅入深地详解 React 的代码编写规范。


📋 目录


一、核心六层规范架构

React 代码规范的六个核心层次,构成了一个完整、健壮的应用开发体系:

React 代码规范六层体系
├── L1: 基础与核心规范 (命名、文件组织、JSX、PropTypes)
│   └── 目标: 代码一致性与可读性
├── L2: 组件设计模式 (组件拆分、组合、解耦)
│   └── 目标: 可复用与可维护性
├── L3: 状态管理规范 (State原则、Reducer、Context、Redux)
│   └── 目标: 数据流清晰与可预测
├── L4: 样式与CSS策略 (CSS Modules、Styled-Components、方案选型)
│   └── 目标: 样式可控与避免冲突
├── L5: 性能优化指南 (Memo、Callback、懒加载、列表优化)
│   └── 目标: 应用流畅与用户体验
└── L6: 项目结构与架构 (目录组织、路由、配置、静态资源)
    └── 目标: 项目可扩展与易于协作

二、L1 - 基础与核心规范

这是最基础也是必须遵守的规范,保证了代码的一致性和可读性。

1. 命名规范 (Naming Conventions)

组件命名

使用 PascalCase (大驼峰命名法),且名称与文件名一致。

// ✅ 正确
// UserProfile.tsx
function UserProfile() { ... }

// ❌ 错误
// userProfile.tsx
function user_profile() { ... }
function User_Profile() { ... }

属性命名

使用 camelCase (小驼峰命名法)。

// ✅ 正确
<Button onClick={handleClick} userName={userName} />

// ❌ 错误
<Button ONCLICK={handleClick} UserName={userName} />

自定义事件处理函数

handle 开头,后接事件名或操作名。

const handleInputChange = () => { ... };
const handleSubmit = () => { ... };
const handleDeleteUser = () => { ... };

布尔型 Props

前缀使用 is, has, should 等,使其语义更明确。

// ✅ 正确
<Button isDisabled={true} hasError={false} shouldShow={true} />

// ❌ 错误
<Button disabled={true} error={false} show={true} />

常量命名

使用 UPPER_SNAKE_CASE (全大写下划线分隔)。

const MAX_COUNT = 10;
const API_BASE_URL = "https://api.example.com";
const DEFAULT_TIMEOUT = 5000;

2. 文件组织 (File Organization)

文件命名

  • 组件文件:使用 PascalCaseUserProfile.tsx
  • 工具函数:使用 camelCaseformatDate.ts
  • 常量文件:使用 UPPER_SNAKE_CASEAPI_CONSTANTS.ts
  • 样式文件:使用 kebab-case 或与组件同名,如 user-profile.module.scssUserProfile.module.scss

目录结构

src/
├── components/          # 通用组件
│   ├── Button/
│   │   ├── index.tsx
│   │   ├── Button.module.scss
│   │   └── types.ts
│   └── Modal/
├── pages/              # 页面组件
│   ├── Home/
│   └── Profile/
├── hooks/              # 自定义 Hooks
├── utils/              # 工具函数
├── store/              # 状态管理
├── api/                # API 接口
└── types/              # TypeScript 类型定义

3. JSX 编写规范

基本规则

// ✅ 正确:自闭合标签
<img src={avatar} alt="Avatar" />
<input type="text" value={value} />

// ❌ 错误
<img src={avatar} alt="Avatar"></img>
<input type="text" value={value}></input>

条件渲染

// ✅ 推荐:使用逻辑与运算符
{
  isLoading && <LoadingSpinner />;
}

// ✅ 推荐:三元运算符(简单条件)
{
  isLoggedIn ? <UserMenu /> : <LoginButton />;
}

// ✅ 推荐:提前返回(复杂条件)
if (!user) return <LoginPrompt />;
return <Dashboard />;

列表渲染

// ✅ 正确:使用 key
{
  users.map(user => <UserItem key={user.id} user={user} />);
}

// ❌ 错误:使用索引作为 key除非列表是静态的
{
  users.map((user, index) => <UserItem key={index} user={user} />);
}

属性展开

// ✅ 正确:使用展开运算符
const props = { name: "John", age: 30 };
<UserProfile {...props} />

// ❌ 错误:逐个传递
<UserProfile name={props.name} age={props.age} />

4. Props 类型检查

TypeScript 类型定义

// ✅ 推荐:使用 TypeScript
interface UserProfileProps {
  name: string;
  age: number;
  email?: string;
  isActive: boolean;
  onUpdate: (id: number) => void;
}

const UserProfile: React.FC<UserProfileProps> = ({
  name,
  age,
  email,
  isActive,
  onUpdate,
}) => {
  // ...
};

默认值

// ✅ 使用默认参数
const UserProfile: React.FC<UserProfileProps> = ({
  name,
  age,
  email = "",
  isActive = false,
  onUpdate,
}) => {
  // ...
};

// 或使用 defaultProps不推荐TypeScript 中已废弃)

三、L2 - 组件设计模式

1. 组件拆分与组合 (Component Splitting & Composition)

单一职责原则

每个组件应该只做一件事。

// ❌ 错误:组件职责过多
const UserDashboard = () => {
  return (
    <div>
      <UserProfile />
      <UserSettings />
      <UserMessages />
      <UserNotifications />
    </div>
  );
};

// ✅ 正确:拆分为多个小组件
const UserDashboard = () => {
  return (
    <div>
      <UserProfile />
      <UserSettings />
      <UserMessages />
      <UserNotifications />
    </div>
  );
};

组件大小控制

  • 单个组件文件不超过 300 行
  • 如果超过,考虑拆分为多个子组件
  • 复杂逻辑提取为自定义 Hook
// ✅ 正确:使用自定义 Hook 提取逻辑
const useUserData = (userId: number) => {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchUser(userId).then(data => {
      setUser(data);
      setLoading(false);
    });
  }, [userId]);

  return { user, loading };
};

const UserProfile = ({ userId }: { userId: number }) => {
  const { user, loading } = useUserData(userId);
  // ...
};

2. 展示组件与容器组件 (Presentational vs Container Components)

展示组件 (Presentational Components)

  • 只负责 UI 渲染
  • 通过 props 接收数据和回调
  • 不依赖业务逻辑
// ✅ 展示组件
interface ButtonProps {
  label: string;
  onClick: () => void;
  disabled?: boolean;
}

const Button: React.FC<ButtonProps> = ({ label, onClick, disabled }) => {
  return (
    <button onClick={onClick} disabled={disabled}>
      {label}
    </button>
  );
};

容器组件 (Container Components)

  • 负责数据获取和状态管理
  • 包含业务逻辑
  • 将数据传递给展示组件
// ✅ 容器组件
const UserListContainer = () => {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchUsers().then(data => {
      setUsers(data);
      setLoading(false);
    });
  }, []);

  if (loading) return <LoadingSpinner />;

  return <UserList users={users} />;
};

四、L3 - 状态管理规范

1. State 放置原则

状态提升 (Lifting State Up)

将共享状态提升到最近的公共父组件。

// ❌ 错误:状态分散
const ChildA = () => {
  const [sharedValue, setSharedValue] = useState("");
  // ...
};

const ChildB = () => {
  const [sharedValue, setSharedValue] = useState("");
  // ...
};

// ✅ 正确:状态提升
const Parent = () => {
  const [sharedValue, setSharedValue] = useState("");
  return (
    <>
      <ChildA value={sharedValue} onChange={setSharedValue} />
      <ChildB value={sharedValue} onChange={setSharedValue} />
    </>
  );
};

状态位置判断

  • 组件内部状态:只在该组件内使用
  • 共享状态:提升到公共父组件或使用全局状态管理
  • 服务器状态:使用 React Query 或 SWR

2. 使用 useReducer 管理复杂状态

当状态逻辑复杂时,使用 useReducer 替代多个 useState

// ✅ 使用 useReducer
interface State {
  count: number;
  step: number;
}

type Action =
  | { type: "increment" }
  | { type: "decrement" }
  | { type: "reset" }
  | { type: "setStep"; step: number };

const reducer = (state: State, action: Action): State => {
  switch (action.type) {
    case "increment":
      return { ...state, count: state.count + state.step };
    case "decrement":
      return { ...state, count: state.count - state.step };
    case "reset":
      return { ...state, count: 0 };
    case "setStep":
      return { ...state, step: action.step };
    default:
      return state;
  }
};

const Counter = () => {
  const [state, dispatch] = useReducer(reducer, { count: 0, step: 1 });
  // ...
};

3. 全局状态管理

Zustand (本项目使用)

// ✅ 使用 Zustand
import { create } from "zustand";

interface UserStore {
  user: User | null;
  setUser: (user: User) => void;
  clearUser: () => void;
}

const useUserStore = create<UserStore>(set => ({
  user: null,
  setUser: user => set({ user }),
  clearUser: () => set({ user: null }),
}));

// 使用
const UserProfile = () => {
  const user = useUserStore(state => state.user);
  const setUser = useUserStore(state => state.setUser);
  // ...
};

Context API

适用于中等规模的全局状态。

// ✅ 使用 Context
const UserContext = createContext<UserContextType | null>(null);

export const UserProvider: React.FC<{ children: React.ReactNode }> = ({
  children,
}) => {
  const [user, setUser] = useState<User | null>(null);

  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
};

五、L4 - 样式与CSS策略

1. CSS Modules (推荐)

本项目使用 CSS Modules 配合 SCSS

// ✅ 使用 CSS Modules
import styles from "./UserProfile.module.scss";

const UserProfile = () => {
  return <div className={styles.container}>...</div>;
};
// UserProfile.module.scss
.container {
  padding: 16px;
  background-color: #fff;

  .title {
    font-size: 24px;
    font-weight: bold;
  }
}

优势

  • 样式作用域隔离,避免冲突
  • 支持 TypeScript 类型检查
  • 支持 SCSS 嵌套和变量
  • 构建时优化,自动移除未使用的样式

2. 样式命名规范

// ✅ 推荐BEM 命名法
.user-profile {
  &__header {
    // ...
  }

  &__body {
    // ...
  }

  &__footer {
    // ...
  }

  &--active {
    // ...
  }
}

3. 样式变量

// ✅ 使用 SCSS 变量
$primary-color: #1890ff;
$secondary-color: #52c41a;
$font-size-base: 14px;

.button {
  background-color: $primary-color;
  font-size: $font-size-base;
}

六、L5 - 性能优化指南

1. 避免不必要的重新渲染

使用 React.memo

// ✅ 使用 React.memo 优化子组件
const UserItem = React.memo(
  ({ user }: { user: User }) => {
    return <div>{user.name}</div>;
  },
  (prevProps, nextProps) => {
    // 自定义比较函数
    return prevProps.user.id === nextProps.user.id;
  },
);

使用 useMemo

// ✅ 使用 useMemo 缓存计算结果
const ExpensiveComponent = ({ items }: { items: Item[] }) => {
  const sortedItems = useMemo(() => {
    return items.sort((a, b) => a.price - b.price);
  }, [items]);

  return <div>{/* 使用 sortedItems */}</div>;
};

使用 useCallback

// ✅ 使用 useCallback 缓存函数
const Parent = () => {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    setCount(prev => prev + 1);
  }, []);

  return <Child onClick={handleClick} />;
};

2. 代码分割与懒加载 (Lazy Loading)

// ✅ 使用 React.lazy 和 Suspense
import { lazy, Suspense } from "react";

const UserProfile = lazy(() => import("./pages/UserProfile"));

const App = () => {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <UserProfile />
    </Suspense>
  );
};

3. 列表渲染优化

虚拟列表

对于长列表,使用虚拟滚动。

// ✅ 使用虚拟列表组件
import { FixedSizeList } from "react-window";

const VirtualizedList = ({ items }: { items: Item[] }) => {
  return (
    <FixedSizeList
      height={600}
      itemCount={items.length}
      itemSize={50}
      width="100%"
    >
      {({ index, style }) => (
        <div style={style}>
          <Item item={items[index]} />
        </div>
      )}
    </FixedSizeList>
  );
};

七、L6 - 项目结构与架构

1. 功能分区目录结构 (Feature-Based Structure)

本项目采用功能分区 + 类型分区的混合结构:

src/
├── components/          # 通用组件(可复用)
│   ├── Button/
│   ├── Modal/
│   └── ...
├── pages/              # 页面组件(路由级别)
│   ├── Home/
│   ├── Profile/
│   └── ...
├── hooks/              # 自定义 Hooks
│   ├── useAuth.ts
│   └── useLocalStorage.ts
├── utils/              # 工具函数
│   ├── formatDate.ts
│   └── validate.ts
├── store/              # 状态管理Zustand
│   ├── module/
│   │   ├── user.ts
│   │   └── weChat.ts
│   └── index.ts
├── api/                # API 接口
│   ├── request.ts
│   └── module/
│       └── wechat.ts
├── types/              # TypeScript 类型定义
│   ├── user.ts
│   └── weChat.ts
└── router/             # 路由配置
    ├── index.tsx
    └── module/

2. 使用绝对路径导入

本项目已配置路径别名 @ 指向 src 目录。

// ✅ 使用绝对路径
import { Button } from "@/components/Button";
import { useUserStore } from "@/store/module/user";
import { formatDate } from "@/utils/formatDate";

// ❌ 避免相对路径
import { Button } from "../../../components/Button";

3. 组件导出规范

// ✅ 推荐:使用 index.ts 统一导出
// components/Button/index.tsx
export { default } from "./Button";
export type { ButtonProps } from "./types";

// 使用
import Button from "@/components/Button";
import type { ButtonProps } from "@/components/Button";

4. 类型定义规范

// ✅ 类型定义文件
// types/user.ts
export interface User {
  id: number;
  name: string;
  email: string;
}

export type UserRole = "admin" | "user" | "guest";

// 使用
import type { User, UserRole } from "@/types/user";

📝 总结

核心原则

  1. 一致性:保持代码风格和命名规范的一致性
  2. 可读性:代码应该易于理解和维护
  3. 可复用性:组件和函数应该尽可能可复用
  4. 性能:关注性能优化,但不要过度优化
  5. 类型安全:充分利用 TypeScript 的类型检查

检查清单

在提交代码前,确保:

  • 组件命名符合 PascalCase
  • 函数命名符合 camelCase
  • 使用了 TypeScript 类型定义
  • 样式使用了 CSS Modules
  • 避免了不必要的重新渲染
  • 使用了绝对路径导入
  • 代码通过了 ESLint 检查
  • 代码通过了 Prettier 格式化

参考资源


最后更新: 2025-01-XX 维护者: 开发团队