
name: react-code-conventiondescription: React TypeScript 项目代码规范。用于新项目搭建、代码评审、重构时统一目录结构、命名、组件拆分、业务分层和可读性规则。强调像人写的代码——减少 AI 味、避免过度抽象。适用场景新建 React TypeScript 前端项目时作为架构基线代码评审时作为风格门禁重构老旧项目时作为目标规范AI 辅助编码时要求 AI 按此规范输出一、目录架构精简版project/ ├── docs/# 设计文档、PRD、代码规范│ ├── design/# 页面/模块设计文档│ ├── code-style/# 代码规范本文件│ └── components/# 公共组件文档├── src/ │ ├── main.tsx# 应用入口│ ├── main.less# 基础样式font/box-sizing/body│ ├── api/# 全局 API 请求层│ │ ├── request.ts# 请求客户端axios 封装│ │ └── user.ts# 按模块拆分│ ├── components/# 跨页面公共组件条件≥2 个页面复用│ │ └──comp/ │ │ ├── index.tsx │ │ ├── index.less │ │ └── models.ts │ ├── pages/# 页面模块│ │ └──page/ │ │ ├── index.tsx# 页面入口装配主流程│ │ ├── index.module.less# 页面样式│ │ ├── api.ts# 页面专属接口│ │ ├── components/# 页面内部组件│ │ ├── hooks/# 页面 hook│ │ └── view.ts# 表格列/表单 schema 等展示结构│ ├── services/# 业务流程层多接口流程、跨页面用例│ ├── stores/# 全局状态│ ├── models/# 跨页面共享数据模型│ ├── lib/# 纯工具函数无 React 依赖│ ├── styles/# 全局样式 token / 主题变量│ └── router/# 路由配置├── __tests__/# 单元测试镜像 src 结构└── mock/# 接口 mock核心原则业务实现优先靠近使用场景确认复用后再上提。二、命名规范文件与目录类型规则示例所有源文件kebab-caseincident-reference.ts,user-avatar.tsx页面目录kebab-case与路由 key 一致src/pages/incident-detail/组件目录kebab-casesrc/components/table-list/样式文件index.less公共或*.module.less页面index.module.less类型声明kebab-case.d.tswasm-exec.d.ts代码标识符类型规则示例React 组件PascalCaseIncidentDetailPageHookuse前缀 camelCaseuseIncidentDetail普通函数camelCase动词开头buildQueryParams,formatTimestamp类型/接口PascalCaseIncidentFormModel常量UPPER_SNAKE_CASE或camelCaseMAX_FILE_SIZE事件处理on 名词 动词onSelectRecord,onConfirmDelete三、文件规模约束文件类型上限超出后拆分为普通.ts/.tsx300 行components/,hooks/,utils.ts页面入口index.tsx500 行components/,view.ts仓储/Store600 行mapper.ts,selectors.ts,actions.ts.less800 行按组件/区块就近拆分四、组件规范页面入口组件// ✅ 好的页面入口装配主流程不堆细节 function IncidentDetailPage() { const detail useIncidentDetail(id); const { canEdit, isClosed } detail.state; return ( div className{styles.page} IncidentHeader event{detail.event} canEdit{canEdit} / IncidentTimeline entries{detail.timeline} / IncidentEditModal open{editing} onSubmit{detail.save} / /div ); }// ❌ 不好的页面入口堆大段 JSX、表格列、内联逻辑 function IncidentDetailPage() { return ( div Table columns{[ { title: 状态, dataIndex: status, render: (v) { if (v open) return Tag colorred.../Tag // ... 80 行 JSX 堆在入口 }} ]}/ /div ); }Props 约定Props 用明确命名的对象类型不用裸字段散列超过 6 个 props 必须定义显式 Props 类型超过 8 个 props 评估拆分或分组回调超过 3 个时合并为actions对象透传完整 store/DTO 是反模式——传入组件真正需要的 view model// ✅ 分组 props type IncidentHeaderProps { event: IncidentView; actions: { onEdit: () void; onClose: () void; onReopen: () void; }; }; // ❌ 透传完整 store type IncidentHeaderProps { store: IncidentStore; // 组件不需要整个 store };组件拆分原则一个组件只处理一个明确功能点80 行以上 JSX 片段必须拆分不为可能复用提前抽象不拆出只转发 props 的薄壳组件五、业务分层请求层(api/)→ 只做 HTTP 调用返回 DTO ↓ 服务层(services/)→ 组合多接口、业务编排 ↓ 状态层(stores/)→ 全局/页面状态管理 ↓ Hook 层(hooks/)→ 临时状态、副作用编排 ↓ 视图层(pages/components/)→ 纯展示和交互入口分层红线同一文件不得混放请求 状态 渲染三种职责。六、反 AI 味规则这些是 AI 生成代码的常见坏味道必须避免AI 味人味替代冗余空值兜底data?.result ?? {} || {}按 DTO 定义信任字段不堆叠兜底泛称命名handleClick,processData,doAction动词名词目标submitCreateForm,toggleAttachmentDelete过度抽象为了灵活性加多层 wrapper直接解决问题确认复用后再提无意义注释// 处理点击事件只写 Why 不写 What函数名已说明 What散落any类型显式类型或unknown 类型守卫useEffect里写业务规则业务规则放普通函数/selectoreffect 只做副作用编排组件内写 API 请求按分层规则——api 层请求hook/store 触发div模拟按钮用 Ant Design Button/Link/Dropdown 等语义组件七、执行路径可读性函数应像菜谱一样线性可读——输入 → 校验 → 处理 → 输出// ✅ 线性可读asyncfunctionsaveIncident(input:SaveInput){constvalidationvalidateSaveInput(input);if(!validation.ok)returnshowError(validation);constpayloadbuildSavePayload(input);constresultawaitrequestSave(payload);returnhandleSaveResult(result);}// ❌ 分支嵌套 内联副作用asyncfunctionsaveIncident(input:SaveInput){if(input.name){constxawaitapi.post(/save,{name:input.name}).then(r{if(r.data.status0){message.success(ok);router.push(/list);}else{if(r.data.msg)message.error(r.data.msg);}});}}八、样式规则场景方案公共组件index.less 全局 class 前缀如app-table-*页面/布局*.module.less局部作用域颜色必须用 token 变量禁止裸色值主题亮/暗色通过 CSS 变量:root[data-themedark]切换九、质量门禁提交前必须通过npmrun typecheck# tsc --noEmitnpmrun lint# eslintnpmtest# 至少覆盖修改文件新增功能必须有对应单元测试修改接口必须同步更新 mockPR 描述必须列出改动文件和影响范围