当前位置: 首页 > news >正文

React + TypeScript 企业级编码规范指南

🎯 为什么需要编码规范?

问题现状

  • 没有规范 → 代码像"杂乱的拼图",难以拼接和维护
  • 风格混乱 → 团队协作像"多国语言会议",沟通成本极高
  • 类型滥用 → TypeScript 优势无法发挥,像"有地图却迷路"
  • 性能问题 → 不必要的重渲染和内存泄漏,像"漏水的容器"

规范价值

  • 统一规范 → 团队协作如"精心编排的交响乐",和谐有序
  • 可维护性 → 代码修改像"模块化积木",轻松重构
  • 可读性 → 新成员理解代码像"阅读优美的散文",流畅自然
  • 质量保障 → 减少潜在 bug,构建"坚实的代码地基"

业界领袖观点

  • “好的代码,应该像诗一样优雅。” — 尤雨溪 (Vue.js 创作者)
  • “规范不是限制创造力的牢笼,而是提升工程效率的翅膀。” — Dan Abramov (Redux 作者)
  • “在软件工程中,一致性比个人风格更重要。” — Martin Fowler (重构之父)

🗂️ 文件组织规范

1.1 单一模块原则

// ✅ Good: 一个文件一个主要组件
// UserProfile.tsx
function UserProfile() {return <div>User Profile</div>;
}// 可以在同一文件定义辅助组件
function UserAvatar() {return <img src="avatar.jpg" alt="User Avatar" />;
}function UserStats() {return <div>User Statistics</div>;
}export default UserProfile;// ❌ Bad: 多个主要组件混合
function UserProfile() { ... }
function ProductCard() { ... } // 不应该在同一文件
export default UserProfile;

理由:提高可读性、便于单元测试和代码复用。

1.2 目录结构规范

src/
├── components/           # 组件目录
│   ├── common/          # 通用组件 (Button, Input, Modal)
│   ├── business/        # 业务组件 (UserCard, ProductList)
│   ├── layouts/         # 布局组件 (Header, Sidebar, Footer)
│   └── forms/           # 表单组件
├── hooks/               # 自定义 Hooks
├── types/               # 类型定义
├── utils/               # 工具函数
├── services/            # API 服务
├── constants/           # 常量定义
├── styles/              # 样式文件
└── assets/              # 静态资源

1.3 模块导入规范

// ✅ Good: 清晰的导入顺序和分组
// 1. 外部依赖 (React, 第三方库)
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { Button, Input, message } from 'antd';// 2. 类型导入 (使用 import type 优化打包)
import type { User, ApiResponse, PaginationParams } from '@/types';// 3. 工具函数和配置
import { formatDate, debounce, classNames } from '@/utils/helpers';
import { API_ENDPOINTS } from '@/constants/api';// 4. 服务和 API
import { userApi, authApi } from '@/services/api';// 5. 组件
import UserAvatar from './UserAvatar';
import LoadingSpinner from '../common/LoadingSpinner';// 6. 样式 (CSS Modules 优先)
import styles from './UserProfile.module.scss';// ❌ Bad: 混乱的导入顺序
import { formatDate } from '@/utils/helpers';
import React from 'react';
import UserAvatar from './UserAvatar';
import { Button } from 'antd';

⚛️ 组件定义规范

2.1 函数式组件标准写法

// ✅ Good: 完整的类型定义和默认值
interface UserProfileProps {userId: number;userName: string;isActive?: boolean;onUpdate?: (user: User) => void;onDelete?: (userId: number) => void;className?: string;
}const UserProfile: React.FC<UserProfileProps> = ({ userId, userName,isActive = false,onUpdate,onDelete,className = ''
}) => {const [user, setUser] = useState<User | null>(null);const [loading, setLoading] = useState(false);// 使用 useCallback 优化性能const handleUpdate = useCallback((updatedUser: User) => {setUser(updatedUser);onUpdate?.(updatedUser);}, [onUpdate]);return (<div className={`${styles.container} ${className}`}><h2>{userName}</h2><UserAvatar userId={userId} /><UserActions isActive={isActive}onUpdate={handleUpdate}onDelete={onDelete}/></div>);
};export default UserProfile;// ❌ Bad: 箭头函数组件(调试困难)
const UserProfile = ({ userId, userName }) => (<div><h2>{userName}</h2></div>
);

2.2 类组件规范(遗留代码维护)

// ✅ Good: 明确的类型定义和生命周期管理
interface UserListState {users: User[];loading: boolean;error: string | null;pagination: Pagination;
}interface UserListProps {initialPage?: number;pageSize?: number;onUserSelect?: (user: User) => void;
}class UserList extends React.Component<UserListProps, UserListState> {// 默认属性值static defaultProps: Partial<UserListProps> = {initialPage: 1,pageSize: 20};constructor(props: UserListProps) {super(props);this.state = {users: [],loading: false,error: null,pagination: {page: props.initialPage!,pageSize: props.pageSize!,total: 0}};}componentDidMount() {this.fetchUsers();}componentDidUpdate(prevProps: UserListProps, prevState: UserListState) {// 当分页变化时重新获取数据if (prevState.pagination.page !== this.state.pagination.page) {this.fetchUsers();}}componentWillUnmount() {// 清理操作this.abortController?.abort();}private abortController: AbortController | null = null;private fetchUsers = async () => {this.setState({ loading: true, error: null });// 取消之前的请求this.abortController?.abort();this.abortController = new AbortController();try {const { page, pageSize } = this.state.pagination;const response = await userApi.getUsers({ page, pageSize }, {signal: this.abortController.signal});this.setState({ users: response.data,pagination: { ...this.state.pagination, total: response.total }});} catch (error) {if (error.name !== 'AbortError') {this.setState({ error: 'Failed to fetch users' });}} finally {this.setState({ loading: false });}};private handlePageChange = (page: number) => {this.setState(prevState => ({pagination: { ...prevState.pagination, page }}));};render() {const { users, loading, error, pagination } = this.state;const { onUserSelect } = this.props;if (loading) return <LoadingSpinner />;if (error) return <ErrorMessage message={error} />;return (<div className={styles.userList}>{users.map(user => (<UserCard key={user.id} user={user}onSelect={onUserSelect}/>))}<Pagination current={pagination.page}pageSize={pagination.pageSize}total={pagination.total}onChange={this.handlePageChange}/></div>);}
}

2.3 高阶组件(HOC)规范

// ✅ Good: 类型安全的 HOC 实现
function withAuth<P extends object>(WrappedComponent: React.ComponentType<P>,requiredRole?: UserRole
) {// 返回组件的 Props 类型,排除被 HOC 处理的属性type Props = Omit<P, 'user' | 'isAuthenticated'>;const WithAuth: React.FC<Props> = (props) => {const { user, isAuthenticated } = useAuth();// 权限检查if (!isAuthenticated) {return <Redirect to="/login" />;}if (requiredRole && user?.role !== requiredRole) {return <div>Access Denied</div>;}// 类型断言确保属性传递安全return <WrappedComponent {...props as P} user={user} />;};// 设置 displayName 便于调试const wrappedComponentName = WrappedComponent.displayName || WrappedComponent.name || 'Component';WithAuth.displayName = `withAuth(${wrappedComponentName})`;return WithAuth;
}// 使用示例
interface DashboardProps {user: User;dashboardData: DashboardData;
}const Dashboard: React.FC<DashboardProps> = ({ user, dashboardData }) => {return (<div><h1>Welcome, {user.name}</h1>{/* 仪表板内容 */}</div>);
};// 应用 HOC
const ProtectedDashboard = withAuth(Dashboard, 'admin');

2.4 自定义 Hook 规范

// ✅ Good: 完整的自定义 Hook 实现
interface UseUserOptions {autoFetch?: boolean;onSuccess?: (user: User) => void;onError?: (error: string) => void;
}interface UseUserReturn {user: User | null;loading: boolean;error: string | null;updateUser: (updates: Partial<User>) => Promise<void>;refetch: () => Promise<void>;reset: () => void;
}/*** 用户数据管理 Hook* * @param userId - 用户 ID* @param options - 配置选项* @returns 用户状态和管理方法* * @example* const { user, loading, updateUser } = useUser(123, {*   onSuccess: (user) => console.log('User loaded:', user)* });*/
function useUser(userId: number, options: UseUserOptions = {}): UseUserReturn {const {autoFetch = true,onSuccess,onError} = options;const [user, setUser] = useState<User | null>(null);const [loading, setLoading] = useState(false);const [error, setError] = useState<string | null>(null);const fetchUser = useCallback(async () => {if (!userId) return;try {setLoading(true);setError(null);const userData = await userApi.getUser(userId);setUser(userData);onSuccess?.(userData);} catch (err) {const errorMessage = err instanceof Error ? err.message : 'Failed to fetch user';setError(errorMessage);onError?.(errorMessage);} finally {setLoading(false);}}, [userId, onSuccess, onError]);const updateUser = useCallback(async (updates: Partial<User>) => {if (!user) return;try {setLoading(true);const updatedUser = await userApi.updateUser(user.id, updates);setUser(updatedUser);onSuccess?.(updatedUser);} catch (err) {const errorMessage = err instanceof Error ? err.message : 'Failed to update user';setError(errorMessage);onError?.(errorMessage);throw err;} finally {setLoading(false);}}, [user, onSuccess, onError]);const reset = useCallback(() => {setUser(null);setError(null);setLoading(false);}, []);// 自动获取用户数据useEffect(() => {if (autoFetch && userId) {fetchUser();}}, [autoFetch, userId, fetchUser]);return {user,loading,error,updateUser,refetch: fetchUser,reset};
}

🔤 命名规范

3.1 文件命名规范

// ✅ Good: PascalCase 组件文件
UserProfile.tsx
NavigationMenu.tsx
DatePicker.tsx
ErrorBoundary.tsx// ✅ Good: camelCase 工具文件和 Hook 文件
formatDate.ts
apiClient.ts
useLocalStorage.ts
validationUtils.ts// ✅ Good: kebab-case 样式文件和资源
user-profile.module.scss
main-header.component.tsx
icon-arrow-right.svg// ❌ Bad
userProfile.tsx          // 组件应该用 PascalCase
user-profile.tsx         // 应该用 PascalCase
User_Profile.tsx         // 不要用下划线

3.2 组件命名规范

// ✅ Good: PascalCase 组件名
import UserProfile from './UserProfile';
import NavigationMenu from './NavigationMenu';
import DatePicker from './DatePicker';// 使用组件
const userProfile = <UserProfile />;
const navigation = <NavigationMenu items={menuItems} />;// ❌ Bad
import user_profile from './user_profile';
import navigationMenu from './NavigationMenu';const UserProfile = <user_profile />;  // 组件实例应该 camelCase
const Navigation = <navigationMenu />; // 组件名应该 PascalCase

3.3 属性命名规范

// ✅ Good: 语义化属性名
<UserCard userData={user}                    // 明确的数据类型isActive={true}                    // 布尔值前缀 is/has/canisLoading={false}                  // 加载状态hasError={false}                   // 错误状态canEdit={true}                     // 权限控制variant="primary"                  // 样式变体size="large"                       // 尺寸onUserUpdate={handleUpdate}        // 事件处理器前缀 ononDeleteSuccess={handleSuccess}    // 明确的事件结果onSubmit={handleSubmit}            // 表单提交
/>// ❌ Bad: 使用 DOM 关键词或模糊命名
<UserCard style="fancy"              // 应该用 variantclassName="active"         // 应该用 isActiveclick={handleClick}        // 应该用 onClickchange={handleChange}      // 应该用 onChangedata={user}               // 过于模糊
/>

3.4 事件处理函数命名

// ✅ Good: 清晰的命名约定
// 基础事件处理
const handleClick = (event: React.MouseEvent) => { ... };
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { ... };
const handleSubmit = (event: React.FormEvent) => { ... };// 带具体上下文的事件
const handleUserClick = (userId: number, event: React.MouseEvent) => { ... };
const handleInputChange = (value: string, fieldName: string) => { ... };
const handleFormSubmit = (formData: FormData) => { ... };// 异步操作事件
const handleUserDelete = async (userId: number) => {try {await deleteUser(userId);onDeleteSuccess?.(userId);} catch (error) {setError('Delete failed');}
};// ❌ Bad: 模糊的命名
const submit = () => { ... };
const click = (id: number) => { ... };
const change = (val: string) => { ... };

3.5 变量和函数命名

// ✅ Good: 描述性的命名
const userList = users.filter(user => user.isActive);
const isLoading = fetchStatus === 'loading';
const hasPermission = user.role === 'admin';
const formattedDate = formatDate(timestamp, 'YYYY-MM-DD');// 函数命名
const calculateTotalPrice = (items: CartItem[]) => { ... };
const validateEmailFormat = (email: string) => { ... };
const generateReportData = (filters: ReportFilters) => { ... };// ❌ Bad: 模糊的命名
const list = users.filter(u => u.active);  // 什么列表?
const loading = status === 'loading';      // 什么在加载?
const check = (email: string) => { ... };  // 检查什么?

📝 JSX 书写规范

4.1 对齐与格式规范

// ✅ Good: 多行属性对齐
<ModalisOpen={isModalOpen}onClose={handleClose}title="用户设置"size="large"backdrop={true}closeOnOverlayClick={false}showCloseButton={true}
><UserForminitialData={userData}onSubmit={handleSubmit}validationRules={validationRules}submitText="保存更改"cancelText="取消"/>
</Modal>// ✅ Good: 单行组件简洁
<Button variant="primary" onClick={handleClick}>提交
</Button><Avatar src={user.avatar} alt={user.name} size={40} />// ✅ Good: 有子元素的正常缩进
<Card><Card.Header><h3>用户信息</h3></Card.Header><Card.Body><p>姓名: {user.name}</p><p>邮箱: {user.email}</p></Card.Body><Card.Footer><Button onClick={handleEdit}>编辑</Button></Card.Footer>
</Card>// ❌ Bad: 混乱的对齐
<Modal isOpen={isModalOpen} onClose={handleClose}title="用户设置" size="large"><UserForm initialData={userData} onSubmit={handleSubmit}validationRules={validationRules} />
</Modal>

4.2 引号规范

// ✅ Good: JSX 属性双引号,JS/TS 单引号
<Button variant="primary" onClick={handleClick}disabled={isLoading}
>点击我
</Button>// JavaScript/Typescript 使用单引号
const message = 'Hello World';
const className = 'btn-primary';
const style = { color: 'red', fontSize: '14px' };// ❌ Bad
<Button variant='primary' onClick={handleClick}>点击我
</Button>const message = "Hello World";
const style = { color: "red" };

4.3 自闭合标签规范

// ✅ Good: 自闭合标签前加空格
<Input />
<Icon type="user" />
<Image src="avatar.jpg" alt="用户头像" />
<Br />
<Hr />// ❌ Bad: 缺少空格
<Input/>
<Icon type="user"/>
<Image src="avatar.jpg" alt="用户头像"/>

4.4 布尔值属性规范

// ✅ Good: 布尔值属性可省略值
<Modal visible />           // 等同于 visible={true}
<Button disabled />         // 等同于 disabled={true}
<Input required />          // 等同于 required={true}
<Checkbox checked />        // 等同于 checked={true}// 明确的 false 值需要显式写出
<Modal visible={false} />
<Button disabled={false} />// ❌ Bad: 多余的 true 值
<Modal visible={true} />
<Button disabled={true} />
<Input required={true} />

4.5 无障碍访问规范

// ✅ Good: 完整的无障碍支持
// 图片必须有 alt 属性
<img src="user-avatar.jpg" alt="用户头像 - 张三"width={64}height={64}loading="lazy"
/>// 按钮必须有明确的标签
<button onClick={handleDelete}aria-label="删除用户"aria-describedby="delete-help-text"disabled={isDeleting}
>{isDeleting ? '删除中...' : '删除'}
</button>
<div id="delete-help-text">永久删除用户,此操作不可撤销</div>// 表单元素必须有标签关联
<label htmlFor="username-input">用户名</label>
<input id="username-input"type="text"aria-required="true"aria-invalid={!!errors.username}aria-describedby="username-error"
/>// 装饰性图片
<img src="background-pattern.png" alt=""role="presentation"
/>// ❌ Bad: 缺少无障碍支持
<img src="avatar.jpg" />
<button onClick={handleClick}>🗑️</button>
<input type="text" />

🏷️ TypeScript 类型规范

5.1 接口与类型定义规范

// ✅ Good: 清晰的接口定义
interface User {// 必需属性id: number;name: string;email: string;// 可选属性avatar?: string;phone?: string;// 枚举-like 属性role: UserRole;status: 'active' | 'inactive' | 'suspended';// 日期类型createdAt: Date;updatedAt: Date;// 嵌套对象profile: {bio: string;website?: string;location: string;};// 数组类型tags: string[];
}// ✅ Good: 使用 type 定义联合类型和工具类型
type UserRole = 'admin' | 'user' | 'guest' | 'moderator';type ApiResponse<T = unknown> = {data: T;success: boolean;message?: string;timestamp: Date;
};type PaginationParams = {page: number;pageSize: number;sortBy?: string;sortOrder?: 'asc' | 'desc';
};// 工具类型
type PartialUser = Partial<User>;
type UserPreview = Pick<User, 'id' | 'name' | 'avatar' | 'role'>;
type UserWithoutId = Omit<User, 'id'>;// ❌ Bad: 使用 any 或过于宽泛的类型
interface BadUser {id: any;           // 应该明确类型data: object;      // 过于宽泛metadata: any;     // 应该定义具体结构
}

5.2 组件 Props 类型规范

// ✅ Good: 完整的 Props 类型定义
interface SearchInputProps {// 必需属性value: string;onChange: (value: string) => void;// 可选属性placeholder?: string;disabled?: boolean;autoFocus?: boolean;maxLength?: number;// 样式相关className?: string;style?: React.CSSProperties;size?: 'small' | 'medium' | 'large';variant?: 'outline' | 'filled' | 'flushed';// 事件处理onSearch?: (value: string) => void;onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;// 功能扩展showClearButton?: boolean;loading?: boolean;icon?: React.ReactNode;
}const SearchInput: React.FC<SearchInputProps> = ({value,onChange,placeholder = '搜索...',disabled = false,autoFocus = false,maxLength,className = '',style,size = 'medium',variant = 'outline',onSearch,onFocus,onBlur,onKeyDown,showClearButton = true,loading = false,icon
}) => {const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {if (event.key === 'Enter') {onSearch?.(value);}onKeyDown?.(event);};const handleClear = () => {onChange('');onSearch?.('');};return (<div className={`${styles.container} ${styles[size]} ${styles[variant]} ${className}`}>{icon && <span className={styles.icon}>{icon}</span>}<inputtype="text"value={value}onChange={(e) => onChange(e.target.value)}placeholder={placeholder}disabled={disabled}autoFocus={autoFocus}maxLength={maxLength}onFocus={onFocus}onBlur={onBlur}onKeyDown={handleKeyDown}className={styles.input}style={style}/>{showClearButton && value && (<button onClick={handleClear}className={styles.clearButton}aria-label="清除搜索">×</button>)}{loading && <div className={styles.spinner} />}</div>);
};

5.3 事件类型定义规范

// ✅ Good: 明确的事件类型定义
interface FormEvents {// 表单事件onSubmit: (event: React.FormEvent<HTMLFormElement>) => void;onReset: (event: React.FormEvent<HTMLFormElement>) => void;// 输入事件onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;onInput: (event: React.FormEvent<HTMLInputElement>) => void;// 焦点事件onFocus: (event: React.FocusEvent<HTMLInputElement>) => void;onBlur: (event: React.FocusEvent<HTMLInputElement>) => void;// 鼠标事件onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;onDoubleClick: (event: React.MouseEvent<HTMLDivElement>) => void;onMouseEnter: (event: React.MouseEvent<HTMLElement>) => void;onMouseLeave: (event: React.MouseEvent<HTMLElement>) => void;// 键盘事件onKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => void;onKeyUp: (event: React.KeyboardEvent<HTMLInputElement>) => void;onKeyPress: (event: React.KeyboardEvent<HTMLInputElement>) => void;
}// 自定义事件类型
interface CustomEvents {onUserSelect: (user: User, event: React.MouseEvent) => void;onDataLoad: (data: unknown[], error?: Error) => void;onStateChange: <T>(key: string, value: T, previousValue: T) => void;
}// 使用示例
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {event.preventDefault();const formData = new FormData(event.currentTarget);// 处理提交逻辑
};const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {const { name, value, type, checked } = event.target;setFormData(prev => ({...prev,[name]: type === 'checkbox' ? checked : value}));
};const handleUserClick = (user: User, event: React.MouseEvent) => {event.stopPropagation();onUserSelect?.(user);
};

5.4 泛型组件规范

// ✅ Good: 类型安全的泛型组件
interface ListProps<T> {// 数据相关items: T[];loading?: boolean;emptyMessage?: string;// 渲染相关renderItem: (item: T, index: number) => React.ReactNode;keyExtractor: (item: T) => string | number;// 布局相关direction?: 'vertical' | 'horizontal';gap?: number;className?: string;// 功能相关onItemClick?: (item: T, index: number) => void;loadMore?: () => void;hasMore?: boolean;
}/*** 通用列表组件* * @template T - 列表项数据类型*/
function GenericList<T>({items,loading = false,emptyMessage = "暂无数据",renderItem,keyExtractor,direction = 'vertical',gap = 8,className = '',onItemClick,loadMore,hasMore = false
}: ListProps<T>) {if (loading && items.length === 0) {return <LoadingSpinner />;}if (items.length === 0) {return (<div className={styles.emptyState}>{emptyMessage}</div>);}const containerStyle = {flexDirection: direction === 'horizontal' ? 'row' : 'column',gap: `${gap}px`} as React.CSSProperties;return (<div className={`${styles.container} ${className}`}><div className={styles.list} style={containerStyle}>{items.map((item, index) => (<divkey={keyExtractor(item)}className={onItemClick ? styles.clickableItem : styles.item}onClick={onItemClick ? () => onItemClick(item, index) : undefined}>{renderItem(item, index)}</div>))}</div>{hasMore && (<div className={styles.loadMore}><Button onClick={loadMore} variant="outline">加载更多</Button></div>)}</div>);
}// 使用示例
interface User {id: number;name: string;email: string;
}// 用户列表
<GenericListitems={users}keyExtractor={(user) => user.id}renderItem={(user) => (<UserCard user={user} />)}onItemClick={(user) => selectUser(user)}
/>// 产品列表
<GenericListitems={products}keyExtractor={(product) => product.sku}renderItem={(product) => (<ProductItem product={product} />)}direction="horizontal"gap={16}
/>

🔄 状态管理规范

6.1 useState 规范

// ✅ Good: 明确的状态类型和初始值
// 基础类型状态
const [count, setCount] = useState<number>(0);
const [name, setName] = useState<string>('');
const [isLoading, setIsLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);// 对象类型状态
interface UserFormState {name: string;email: string;age: number;interests: string[];
}const [formData, setFormData] = useState<UserFormState>({name: '',email: '',age: 0,interests: []
});// 数组类型状态
const [users, setUsers] = useState<User[]>([]);
const [selectedIds, setSelectedIds] = useState<Set<number>>(new Set());// 使用函数初始化复杂状态
const [config, setConfig] = useState<AppConfig>(() => {const saved = localStorage.getItem('app-config');return saved ? JSON.parse(saved) : DEFAULT_CONFIG;
});// ❌ Bad: 隐式的 any 类型
const [user, setUser] = useState(null);        // 应该明确类型
const [data, setData] = useState([]);          // 应该明确数组类型
const [filters, setFilters] = useState({});    // 应该定义接口

6.2 useEffect 规范

// ✅ Good: 清晰的副作用管理和依赖处理
useEffect(() => {let mounted = true;let abortController: AbortController | null = null;const fetchData = async () => {if (!userId) return;try {setIsLoading(true);setError(null);// 创建 AbortController 用于取消请求abortController = new AbortController();const userData = await userApi.getUser(userId, {signal: abortController.signal});// 检查组件是否仍挂载if (mounted) {setUser(userData);onUserLoad?.(userData);}} catch (err) {if (mounted && err.name !== 'AbortError') {setError(err instanceof Error ? err.message : 'Failed to fetch user');}} finally {if (mounted) {setIsLoading(false);}}};fetchData();// 清理函数return () => {mounted = false;abortController?.abort();};
}, [userId, onUserLoad]); // 明确的依赖数组// ✅ Good: 事件监听和定时器管理
useEffect(() => {const handleResize = () => {setWindowSize({width: window.innerWidth,height: window.innerHeight});};const handleKeyPress = (event: KeyboardEvent) => {if (event.key === 'Escape') {onClose?.();}};window.addEventListener('resize', handleResize);document.addEventListener('keydown', handleKeyPress);return () => {window.removeEventListener('resize', handleResize);document.removeEventListener('keydown', handleKeyPress);};
}, [onClose]);// ✅ Good: 条件执行的副作用
useEffect(() => {if (!isOpen) return;// 只在模态框打开时执行的逻辑const timer = setTimeout(() => {setAutoFocus(true);}, 100);return () => clearTimeout(timer);
}, [isOpen]);

6.3 useReducer 规范

// ✅ Good: 复杂状态管理使用 useReducer
interface TodoState {todos: Todo[];filter: 'all' | 'active' | 'completed';loading: boolean;error: string | null;
}type TodoAction =| { type: 'SET_LOADING'; payload: boolean }| { type: 'SET_ERROR'; payload: string | null }| { type: 'ADD_TODO'; payload: Todo }| { type: 'UPDATE_TODO'; payload: { id: number; updates: Partial<Todo> } }| { type: 'DELETE_TODO'; payload: number }| { type: 'SET_FILTER'; payload: TodoState['filter'] }| { type: 'CLEAR_COMPLETED' };const todoReducer = (state: TodoState, action: TodoAction): TodoState => {switch (action.type) {case 'SET_LOADING':return { ...state, loading: action.payload };case 'SET_ERROR':return { ...state, error: action.payload };case 'ADD_TODO':return { ...state, todos: [...state.todos, action.payload] };case 'UPDATE_TODO':return {...state,todos: state.todos.map(todo =>todo.id === action.payload.id? { ...todo, ...action.payload.updates }: todo)};case 'DELETE_TODO':return {...state,todos: state.todos.filter(todo => todo.id !== action.payload)};case 'SET_FILTER':return { ...state, filter: action.payload };case 'CLEAR_COMPLETED':return {...state,todos: state.todos.filter(todo => !todo.completed)};default:return state;}
};// 使用 useReducer
const [state, dispatch] = useReducer(todoReducer, {todos: [],filter: 'all',loading: false,error: null
});// Action creators
const addTodo = (text: string) => {const newTodo: Todo = {id: Date.now(),text,completed: false,createdAt: new Date()};dispatch({ type: 'ADD_TODO', payload: newTodo });
};const toggleTodo = (id: number) => {dispatch({ type: 'UPDATE_TODO', payload: { id, updates: { completed: true } } });
};

⚡ 性能优化规范

7.1 Key 属性规范

// ✅ Good: 稳定的唯一标识
{users.map(user => (<UserCard key={user.id} user={user} />
))}// 复合 key
{items.map(item => (<ListItem key={`${item.type}-${item.id}-${item.version}`} item={item} />
))}// 动态数据使用唯一标识
{messages.map(message => (<Message key={message.id || `temp-${message.timestamp}`} message={message} />
))}// ❌ Bad: 使用索引作为 key
{users.map((user, index) => (<UserCard key={index} user={user} />
))}// ❌ Bad: 不稳定的 key
{products.map(product => (<ProductCard key={product.name} product={product} /> // name 可能重复
))}

7.2 记忆化优化规范

// ✅ Good: 适当的记忆化优化
const ExpensiveComponent: React.FC<ExpensiveComponentProps> = React.memo(({ data, onUpdate,filters 
}) => {// 记忆化回调函数const handleUpdate = useCallback((updatedData: Data) => {onUpdate(updatedData);}, [onUpdate]);// 记忆化复杂计算const processedData = useMemo(() => {return data.filter(item => filters.includes(item.category)).sort((a, b) => a.priority - b.priority).map(item => ({...item,score: calculateScore(item)}));}, [data, filters]);// 记忆化配置对象const chartConfig = useMemo(() => ({type: 'bar' as const,data: processedData,options: {responsive: true,plugins: {legend: {position: 'top' as const,}}}}), [processedData]);return (<div><Chart config={chartConfig} /><DataTable data={processedData} onUpdate={handleUpdate} /></div>);
});// 自定义比较函数
const UserList: React.FC<UserListProps> = React.memo(({ users, selectedUserId }) => {return (<div>{users.map(user => (<UserItem key={user.id}user={user}isSelected={user.id === selectedUserId}/>))}</div>);
}, (prevProps, nextProps) => {// 自定义比较逻辑return (prevProps.selectedUserId === nextProps.selectedUserId &&prevProps.users.length === nextProps.users.length &&prevProps.users.every((user, index) => user.id === nextProps.users[index].id));
});

7.3 代码分割规范

// ✅ Good: 路由级代码分割
const UserManagement = React.lazy(() => import('./pages/UserManagement' /* webpackChunkName: "user-management" */)
);const ProductManagement = React.lazy(() => import('./pages/ProductManagement' /* webpackChunkName: "product-management" */)
);const Settings = React.lazy(() => import('./pages/Settings' /* webpackChunkName: "settings" */)
);// 统一加载状态
const GlobalLoading: React.FC = () => (<div className={styles.globalLoading}><Spin size="large" /><p>加载中...</p></div>
);const App: React.FC = () => {return (<Router><Suspense fallback={<GlobalLoading />}><Routes><Route path="/users/*" element={<UserManagement />} /><Route path="/products/*" element={<ProductManagement />} /><Route path="/settings/*" element={<Settings />} /></Routes></Suspense></Router>);
};// ✅ Good: 组件级代码分割
const HeavyChart = React.lazy(() => import('./components/HeavyChart'));
const DataGrid = React.lazy(() => import('./components/DataGrid'));const Dashboard: React.FC = () => {const [activeTab, setActiveTab] = useState<'chart' | 'table'>('chart');return (<div><TabSwitcher activeTab={activeTab} onChange={setActiveTab} /><Suspense fallback={<ChartSkeleton />}>{activeTab === 'chart' && <HeavyChart />}</Suspense><Suspense fallback={<TableSkeleton />}>{activeTab === 'table' && <DataGrid />}</Suspense></div>);
};// ✅ Good: 预加载策略
const usePreload = (importFn: () => Promise<any>) => {return useCallback(() => {importFn();}, [importFn]);
};// 在需要的地方预加载
const preloadSettings = usePreload(() => import('./pages/Settings'));<Link to="/settings" onMouseEnter={preloadSettings}onFocus={preloadSettings}
>设置
</Link>

🎨 代码风格规范

8.1 导入分组与排序规范

// ✅ Good: 有序的导入分组
// ========================================
// 1. 外部依赖 (React, 第三方库)
// ========================================
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { Router, Route, Routes } from 'react-router-dom';
import { Button, Input, Form, message } from 'antd';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';// ========================================
// 2. 类型导入 (使用 import type)
// ========================================
import type { User, Product, ApiResponse } from '@/types';
import type { RootState, AppDispatch } from '@/store';// ========================================
// 3. 工具函数和配置
// ========================================
import { formatDate, debounce, classNames } from '@/utils/helpers';
import { API_ENDPOINTS, APP_CONFIG } from '@/constants';
import { logger, errorReporter } from '@/utils/logging';// ========================================
// 4. 服务和 API
// ========================================
import { userApi, productApi } from '@/services/api';
import { authService } from '@/services/auth';// ========================================
// 5. Store 和状态管理
// ========================================
import { useAppSelector, useAppDispatch } from '@/store/hooks';
import { selectUser, selectIsLoading } from '@/store/slices/userSlice';// ========================================
// 6. 组件
// ========================================
// 相对路径导入 (当前目录)
import UserAvatar from './UserAvatar';
import UserActions from './UserActions';// 相对路径导入 (上级目录)
import LoadingSpinner from '../common/LoadingSpinner';
import ErrorBoundary from '../common/ErrorBoundary';// 绝对路径导入
import { Button, Modal } from '@/components/common';// ========================================
// 7. 样式和资源
// ========================================
import styles from './UserProfile.module.scss';
import './UserProfile.css'; // 全局样式// ========================================
// 8. 其他
// ========================================
// 最后导入本地工具函数
const { validateEmail, generateId } = require('./utils');

8.2 注释规范

// ✅ Good: JSDoc 风格注释
/*** 用户信息卡片组件* * 显示用户基本信息,支持编辑和删除操作* * @param props - 组件属性* @param props.user - 用户数据对象 (必需)* @param props.onEdit - 编辑用户回调函数* @param props.onDelete - 删除用户回调函数  * @param props.variant - 显示变体,控制布局和样式* @param props.isSelected - 是否被选中状态* @param props.className - 自定义 CSS 类名* * @example* ```tsx* <UserCard*   user={userData}*   onEdit={handleEdit}*   onDelete={handleDelete}*   variant="detailed"*   isSelected={true}*   className="custom-card"* />* ```* * @throws {Error} 当 user 属性未提供时抛出错误* * @returns 用户卡片 React 元素*/
const UserCard: React.FC<UserCardProps> = ({ user,onEdit,onDelete,variant = 'compact',isSelected = false,className = ''
}) => {if (!user) {throw new Error('UserCard: user prop is required');}// 处理编辑操作const handleEdit = useCallback(() => {onEdit?.(user);}, [user, onEdit]);// 处理删除操作const handleDelete = useCallback(() => {if (window.confirm(`确定要删除用户 ${user.name} 吗?`)) {onDelete?.(user.id);}}, [user, onDelete]);return (<div className={classNames(styles.card, className)}>{/* 组件实现 */}</div>);
};// ✅ Good: 行内注释
const calculatePrice = (basePrice: number, discount: number) => {// 确保折扣在 0-1 范围内const validDiscount = Math.max(0, Math.min(1, discount));// 计算最终价格(四舍五入到两位小数)const finalPrice = basePrice * (1 - validDiscount);return Math.round(finalPrice * 100) / 100;
};

8.3 错误边界规范

// ✅ Good: 完整的错误边界实现
interface ErrorBoundaryState {hasError: boolean;error?: Error;errorInfo?: React.ErrorInfo;
}interface ErrorBoundaryProps {children: React.ReactNode;fallback?: React.ComponentType<{ error: Error; resetError: () => void }>;onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
}/*** 错误边界组件* * 捕获子组件树的 JavaScript 错误,显示降级 UI* * @see https://reactjs.org/docs/error-boundaries.html*/
class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {constructor(props: ErrorBoundaryProps) {super(props);this.state = { hasError: false };}static getDerivedStateFromError(error: Error): ErrorBoundaryState {// 更新 state 使下一次渲染能够显示降级 UIreturn { hasError: true, error };}componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {// 记录错误信息console.error('Error caught by boundary:', error, errorInfo);// 上报错误服务this.props.onError?.(error, errorInfo);errorReporter.captureException(error, { extra: errorInfo,tags: { component: 'ErrorBoundary' }});this.setState({error,errorInfo});}// 重置错误状态resetError = () => {this.setState({ hasError: false, error: undefined, errorInfo: undefined });};render() {if (this.state.hasError) {// 自定义降级 UIif (this.props.fallback) {const FallbackComponent = this.props.fallback;return <FallbackComponent error={this.state.error!} resetError={this.resetError} />;}// 默认降级 UIreturn (<div className={styles.errorFallback}><div className={styles.errorContent}><h2>😵 出错了</h2><p>抱歉,发生了意外错误</p>{process.env.NODE_ENV === 'development' && this.state.error && (<details className={styles.errorDetails}><summary>错误详情 (开发模式)</summary><pre>{this.state.error.stack}</pre>{this.state.errorInfo && (<pre>{this.state.errorInfo.componentStack}</pre>)}</details>)}<div className={styles.errorActions}><button onClick={this.resetError}className={styles.retryButton}>重试</button><button onClick={() => window.location.reload()}className={styles.reloadButton}>刷新页面</button></div></div></div>);}return this.props.children;}
}// 使用示例
const App: React.FC = () => {const handleError = (error: Error, errorInfo: React.ErrorInfo) => {// 自定义错误处理逻辑analytics.track('ReactError', {message: error.message,componentStack: errorInfo.componentStack});};return (<ErrorBoundary fallback={CustomErrorFallback}onError={handleError}><Router><AppRoutes /></Router></ErrorBoundary>);
};

📊 总结与工具推荐

核心原则总结

  1. 一致性:团队统一编码风格,降低认知成本
  2. 可读性:代码自解释,减少注释需求
  3. 可维护性:易于修改和扩展,降低技术债务
  4. 类型安全:充分利用 TypeScript,减少运行时错误
  5. 性能友好:避免不必要的重渲染和内存泄漏
  6. 团队协作:统一的规范便于代码审查和知识传递

必备工具配置

// .eslintrc.js
module.exports = {extends: ['eslint:recommended','@typescript-eslint/recommended','plugin:react/recommended','plugin:react-hooks/recommended','plugin:jsx-a11y/recommended'],rules: {'react/prop-types': 'off', // TypeScript 处理类型检查'@typescript-eslint/explicit-function-return-type': 'warn','@typescript-eslint/no-explicit-any': 'warn','jsx-a11y/anchor-is-valid': 'error'}
};// .prettierrc.js
module.exports = {singleQuote: true,trailingComma: 'es5',printWidth: 80,tabWidth: 2,semi: true,bracketSpacing: true,arrowParens: 'avoid',endOfLine: 'lf'
};// package.json scripts
{"scripts": {"lint": "eslint src --ext .ts,.tsx","lint:fix": "eslint src --ext .ts,.tsx --fix","format": "prettier --write src/**/*.{ts,tsx,json,css,scss}","type-check": "tsc --noEmit","pre-commit": "lint-staged"}
}// lint-staged.config.js
module.exports = {'*.{ts,tsx}': ['eslint --fix','prettier --write'],'*.{json,css,scss,md}': ['prettier --write']
};

文件模板示例

// components/ComponentName.tsx
import React from 'react';
import type { ComponentNameProps } from './types';
import styles from './ComponentName.module.scss';/*** 组件描述* * @param props - 组件属性*/
const ComponentName: React.FC<ComponentNameProps> = ({// 属性定义
}) => {// 状态和逻辑return (// JSX);
};export default ComponentName;// hooks/useFeatureName.ts
import { useState, useEffect } from 'react';
import type { UseFeatureNameReturn, UseFeatureNameOptions } from './types';/*** Hook 描述* * @param options - 配置选项*/
export function useFeatureName(options: UseFeatureNameOptions = {}): UseFeatureNameReturn {// Hook 实现return {// 返回值};
}

遵循这些规范,你的 React + TypeScript 代码将具备:

  • ✅ 清晰的组件结构和职责分离
  • ✅ 严格的类型约束和编译时检查
  • ✅ 优秀的运行时性能和用户体验
  • ✅ 良好的团队协作和代码审查体验
  • ✅ 易于维护、测试和扩展的代码基础

规范不是束缚,而是提升开发效率、降低维护成本的基石。让每一行代码都成为艺术品,而非负担。

http://www.dtcms.com/a/507894.html

相关文章:

  • React Router 路由模式详解:HashRouter vs BrowserRouter
  • 福田做网站怎么样下载网站模板
  • 每日一个C语言知识:C 结构体
  • 淘宝网中国站电脑版登录辽宁省建设工程招投标
  • sql数据库语法
  • 使用jmeter进行压力测试
  • 长期网站外包wordpress主题php详解
  • 面试八股 快速讲解 集合类中的 fail-fast和fail-safe
  • MySQL K8S日志分析与数据还原
  • RK3588 RKLLM大语言模型AI开发环境搭建及板端部署
  • Android Studio配置指南:Gradle 安装
  • php能做手机网站吗网站开发 例子
  • 【完整源码+数据集+部署教程】【零售和消费品&存货】快递包裹条形码与二维码识别系统源码&数据集全套:改进yolo11-RFCBAMConv
  • 泉州seo建站wordpress 生成 应用
  • BearPi小熊派 鸿蒙开发入门笔记(3)
  • 欧几里得算法(Euclidean Algorithm):求最大公约数的经典方法
  • MLLM技术报告 核心创新一览
  • C++设计模式_行为型模式_策略模式Strategy
  • **发散创新:多智能体系统的探索与实践**随着人工智能技术的飞速发展,多智能体系统作为当今研究的热点领域,正受到越来越多关注
  • 音乐网站设计企业网络搭建与应用
  • Flink Data Sink 理论 、架构、语义保证、两阶段提交与可插拔拓扑
  • DeviceNet转Ethernet/IP食品饮料包装线码垛机器人高效通信方案
  • 《基于分布式多模态传感模块的全身尺度机器人皮肤:设计、评估与应用》TR-O 2025论文解析
  • 亿万网站网站开发详细流程
  • 还是网站好买一个app软件要多少钱
  • 无锡万度网站建设WordPress Demo演示
  • 智能外呼是什么意思
  • 【读论文】——基于光谱学的玉米种子品质检测及其成像技术综述
  • 如何自建网站满分作文网
  • 服务器/Pytorch——对于只调用一次的函数初始化,放在for训练外面和里面的差异