| name | react-component-diagnosis |
| description | React 组件架构诊断器。从 7 个维度(使用者 API、数据流、可测试性、可扩展性、性能、心智模型、边界契约)深度分析一个 React 组件的设计质量,每个维度 1-5 分评分并给出改进建议。当用户请求组件诊断、组件设计审查、架构分析、组件体检时使用。也适用于用户指向一个组件目录问"这个组件设计得怎么样"、"帮我分析一下这个组件"、"组件质量怎么样"等场景。触发词包括:组件诊断、component diagnosis、组件体检、架构审查、设计分析、component review、组件评审。 |
React 组件架构诊断器
从 7 个维度对一个 React 组件做深度体检,输出结构化诊断报告。
诊断流程
1. 定位组件 → 读取全部源文件(含子目录),不要跳过任何文件
2. 理解组件做什么 → 用一句话概括职责
3. 逐维度审视 → 回答核心问题 + 打分 + 找证据
4. 交叉检查 → 回看每个维度,检查是否有遗漏的具体问题
5. 生成报告 → 评分卡 + 亮点 + 改进建议
深度分析的标准
诊断的价值在于找到不读代码就发现不了的问题。表面的结构评价("分层清晰"、"命名不错")任何人扫一眼目录就能得出。真正有价值的发现来自:
- 逐行阅读关键路径,追踪数据在每一层的具体变换
- 检查 useMemo/useEffect 的依赖列表是否真的稳定
- 追踪类型断言(
as、as never、as any)出现的位置和原因
- 识别每帧/每次渲染中不必要的重复计算
- 检查 cleanup 函数是否真正清理了异步操作
打分时扣分要有底气——找到了具体的代码问题才扣分,而不是"感觉可以更好"就扣。反过来,给高分也要有证据——不能因为"看起来不错"就给 5 分。
七个诊断维度
每个维度回答一个核心问题,评分 1-5:
| 分数 | 含义 |
|---|
| 5 | 教科书级别,可作为团队标杆 |
| 4 | 扎实,有小瑕疵但不影响大局 |
| 3 | 及格,能用但有明显改进空间 |
| 2 | 问题突出,会拖慢开发或埋坑 |
| 1 | 需要重写或大幅重构 |
打分要基于具体代码证据,不要凭感觉。同一个问题不要在多个维度重复计分——归到最相关的那个维度。
1. 使用者 API(Consumer API)
核心问题:调用者能用最少的认知负担做出正确的事情吗?
审视点:
- 最小可用示例:需要传几个 props 才能跑起来?超过 3 个必填 props 就要警惕
- 默认值覆盖:80% 的使用场景能零配置吗?检查是否有合理的 defaultProps 或参数默认值
- 类型自文档化:Props 类型名是否让人一看就懂?是否区分了"输入类型"(宽松)和"内部类型"(严格)?
- 错误引导:传错参数时,报错信息是否指向修复方向?还是只丢一个 TypeScript 类型错误?
- API 一致性:命名风格是否统一?回调是
onXxx 还是 handleXxx?状态 prop 和控制 prop 是否有清晰的模式?
高分信号:
- 有 preset/factory 函数简化常见配置
- Props 接口有 JSDoc 但不啰嗦
- 导出了 estimate/utility 函数让调用者可以做计算
低分信号:
- 必填 props 超过 5 个
- 布尔 props 用双重否定(noDisableX)
- 同一个概念有多种配置方式且文档没说清楚选哪个
2. 数据流(Data Flow)
核心问题:数据从 props 到渲染输出,变换链条是否清晰、单向、可追踪?
审视点:
- 变换层次:数据经过几层变换?每层的输入/输出类型是否明确?
- 单向性:是否存在反向回调修改上游数据?是否有隐式双向绑定?
- 副作用隔离:纯计算和副作用(API 调用、DOM 操作、定时器)是否分开?
- 中间产物:变换中间的数据是否可序列化、可 console.log 调试?
- 状态位置:状态放在了正确的层级吗?有没有不必要地提升或下沉?
高分信号:
- 纯计算逻辑抽到独立函数/文件,不依赖 React
- 数据变换像流水线:A → B → C → render,每步类型明确
- useEffect 只处理真正的副作用,不用来做数据派生
低分信号:
- 组件内部有复杂的 useEffect 链互相触发
- 同一份数据在多个 state 里冗余存储
- props 传入后被 useState 复制一份(同步 props→state 反模式)
3. 可测试性(Testability)
核心问题:能否在不启动浏览器的情况下验证核心逻辑?
审视点:
- 纯函数比例:核心业务逻辑中,纯函数占多少?纯函数可以直接单测,不需要 mock
- 依赖可替换性:外部依赖(API、存储、第三方库)是否可以在测试中替换?
- 测试存在性:有没有测试?测试覆盖的是核心路径还是边边角角?
- 测试粒度:测试粒度是否匹配代码变更频率?变更最频繁的模块测试是否最厚?
- 边界覆盖:空输入、零值、越界、异常路径是否有覆盖?
高分信号:
- 核心逻辑在独立文件中,import 进来就能测
- 测试文件和源文件同目录或紧邻
- 测试描述读起来像规格说明
低分信号:
- 所有逻辑都在组件里,必须 render 才能测
- 测试严重依赖 mock,mock 比真实代码还多
- 没有测试
4. 可扩展性(Extensibility)
核心问题:加一个新功能,需要改几个文件、碰几个层?
审视点:
- 扩展模式:新增一个变体/类型,是"加一个 case"还是"改一条 if-else 链"?
- 组合优于继承:是否用组合(children、render props、hooks)而非继承来扩展?
- 插件点:是否有明确的扩展点(handler 数组、策略模式、slot 模式)?
- 变更影响面:改一个功能是否会波及到不相关的代码?
- 过度设计检测:是否为了"未来可能"加了现在用不到的抽象?
高分信号:
- 用 discriminated union 做类型分发
- 渲染策略通过 handler/plugin 数组扩展
- 新增功能只需要加文件,不需要改现有文件
低分信号:
- 一个巨大的 switch/if-else 散落在多处
- 加一个小功能要改 5+ 个文件
- 有 3 层抽象但只有 1 个实现
5. 性能(Performance)
核心问题:在典型使用场景下,是否存在不必要的计算或渲染?
审视点:
- 渲染效率:是否存在不必要的 re-render?Context 是否拆分得当?
- 计算位置:重计算发生在渲染循环内还是外?是否用了 useMemo/useCallback 在正确的地方?
- 异步处理:大计算是否异步/延迟执行?是否会阻塞首次渲染?
- 内存:是否有未清理的订阅、定时器、事件监听?useEffect cleanup 是否真正取消了进行中的异步操作?
- Bundle 影响:依赖的第三方库体积如何?是否可以 tree-shake?
帧级分析(动画/渲染密集型组件必做)
如果组件涉及动画、视频渲染、或高频更新(如 60fps),需要做帧级分析——追踪每一帧渲染中实际执行了哪些计算:
- 每帧都会执行的代码:在 useCurrentFrame() 驱动的渲染中,哪些 useMemo 的依赖每帧都变?这些 useMemo 内部做了多重的计算?
- 对象创建频率:每帧是否生成新的数组/对象引用?如果连续帧计算结果相同(如动画未推进),是否有跳过机制?
- GC 压力:高频创建的临时对象(如 token 数组切片、字符串 split 结果)是否可以缓存或复用?
- DOM 节点数:是否一次性渲染了大量 DOM 节点(如为每个音效帧创建 Sequence),而非按需渲染?
- 依赖链稳定性:useMemo/useEffect 的依赖中是否包含引用不稳定的对象?即使用 useMemo 包裹了上游计算,重建条件是否足够严格?
高分信号:
- 重计算一次性完成,结果缓存
- Context 按更新频率分离,避免全树刷新
- 大依赖动态 import
- 连续帧结果相同时有 early return 或引用复用
低分信号:
- 每帧/每次渲染都重新计算不变的数据
- 一个 Context 包含所有状态,任何变化全树 re-render
- useEffect 里有未清理的 setInterval
- 每帧 split/slice/concat 创建大量临时对象且无缓存
- useEffect cleanup 只设 cancelled flag 但不取消异步操作
6. 心智模型(Mental Model)
核心问题:一个新人打开代码,能否在 10 分钟内理解"这个东西怎么工作"?
审视点:
- 文件即职责:文件名是否能让人猜到里面做什么?
- 命名一致性:同一个概念在不同文件里叫同一个名字吗?
- 目录结构:目录组织是按职责还是按技术类型?有没有模糊的 utils/helpers 垃圾桶?
- 依赖方向:是否单向?有没有循环依赖?
- 意外最小化:有没有"意外的文件"或"意外的行为"——打开一个文件发现做的事和文件名完全不搭?
高分信号:
- 文件名就是职责描述
- 依赖图是 DAG(有向无环)
- README 或目录结构让人一眼看懂全貌
低分信号:
- 有 utils.ts 超过 200 行
- 同一个概念在不同地方叫不同名字
- 文件之间循环 import
7. 边界与契约(Boundaries & Contracts)
核心问题:这个组件和外部世界的接触面有多小、契约有多严格?
审视点:
- 入口校验:在哪一层做校验?是否只在入口做一次,之后内部信任数据?
- 外部依赖封装:对第三方库的依赖是否有封装层?换库的成本有多大?
- 类型边界:跨层传递数据时,是否有类型变换而不是一路透传同一个类型?
- 类型安全:是否存在
as、as never、as any 类型断言?每处断言的原因是什么——是设计缺陷还是外部类型不匹配的权宜之计?
- 校验一致性:不同入口路径(组件入口 vs 工具函数直接调用)的校验是否一致?是否存在绕过 schema 校验的路径?
- 错误边界:组件崩溃是否有 ErrorBoundary 兜底?错误是否有分级处理?
- 接触点计数:这个组件和外部世界有几个接触点?每个接触点是否可以独立替换?
高分信号:
- Zod/schema 在入口校验,内部不做防御性编程
- 第三方库通过 adapter/wrapper 封装,只暴露需要的接口
- 类型随层级变化:InputProps → ResolvedProps → RenderData
- 零 `as any`,少量 `as` 断言且有明确理由
低分信号:
- 每个函数都做 null check 和类型断言
- 第三方库的类型直接出现在公开 API 里
- 没有 ErrorBoundary,一个子组件崩溃整个页面白屏
- 多处 `as never` / `as any` 强制转型,原因不明
- 不同调用路径对同一数据的校验不一致
报告格式
# 组件诊断报告:{组件名}
> {一句话概括组件职责}
## 评分卡
| 维度 | 得分 | 一句话评价 |
|------|------|-----------|
| 使用者 API | ★★★★☆ | ... |
| 数据流 | ★★★★★ | ... |
| 可测试性 | ★★★★☆ | ... |
| 可扩展性 | ★★★★☆ | ... |
| 性能 | ★★★☆☆ | ... |
| 心智模型 | ★★★★★ | ... |
| 边界契约 | ★★★★☆ | ... |
| **综合** | **★★★★☆ (4.1)** | |
## 逐维度分析
每个维度列出优点和不足,引用具体代码位置(文件名:行号或函数名)。
不足之处要说清楚"是什么"和"为什么是问题",而不是笼统的"可以更好"。
## 亮点
列出 2-3 个具体的设计亮点,引用代码位置。
这些是值得在团队内推广的模式。
## 改进建议
按优先级分三级:
### P0 -- 值得尽快改
会导致 bug、性能问题、或维护隐患的问题。
### P1 -- 建议优化
改了会明显提升代码质量或开发体验。
### P2 -- 锦上添花
有则更好,不改也不会出事。
每条包含:
- **问题**:具体描述,引用代码位置(文件名:行号或函数名)
- **影响**:这个问题会导致什么后果
- **建议**:怎么改,给出具体方向(但不写代码)
- **工作量**:小(< 1h)/ 中(1-4h)/ 大(> 4h)
## 架构图(可选)
如果组件有多层结构,用文本图或 mermaid 画出数据流。
诊断原则
- 证据驱动:每个评分都要有具体代码位置支撑,不要凭印象打分。引用格式:
文件名:行号 或 文件名 中的 函数名
- 深入代码:表面的结构夸奖("分层清晰")价值有限。真正有用的诊断来自逐行阅读关键路径后发现的具体问题——比如 useMemo 依赖列表遗漏、useEffect cleanup 不彻底、类型断言背后的设计妥协
- 上下文感知:一个 50 行的工具组件和一个 2000 行的复合组件,标准不同。简单组件不需要四层流水线,复杂组件没有测试才是问题
- 不重复计分:同一个问题只在最相关的维度扣分
- 建议可执行:改进建议要具体到"怎么改",而不是"应该更好"
- 先褒后贬:先说亮点,再说问题。好的设计值得被看见
- 扣分要有底气,给高分也要有证据:不能因为"没发现问题"就默认给 5 分——5 分意味着在这个维度上找到了值得学习的设计模式