深度诊断单个 React 组件的架构设计质量,产出一份结构化体检报告:7 维度评分卡(使用者 API、数据流、可测试性、可扩展性、性能、心智模型、边界契约,每维 1-5 分 + 代码证据)、设计亮点、按 P0/P1/P2 分级且标注工作量的改进建议——所有评分基于逐行读代码后发现的具体问题(useMemo 依赖、useEffect cleanup、类型断言、每帧重复计算等),而非扫一眼目录得出的表面评价。当用户请求组件诊断、组件设计审查、架构分析、组件体检、组件 API/props 设计评估时使用,即使没说出"诊断"二字也应主动触发:用户指向一个组件目录或 .tsx 文件问"这个组件设计得怎么样"、"帮我看看这个组件"、"这个写得怎么样"、"是否需要重构"、"为什么每次都 re-render"、"useEffect 写对了吗"、"props 设计合理吗",或用英文说 "review this React component"、"is this component well designed"、"audit this component"、"should I refactor this component" 时,都应触发。触发词:组件诊断、组件体检、架构审查、设计分析、组件评审、组件 API、props 设计、组件要不要重构、component diagnosis、component review、react component audit。
Resources
1Install
npx skillscat add hylarucoder/hai-stack/react-component-diagnosis Install via the SkillsCat registry.
React 组件架构诊断器
从 7 个维度对一个 React 组件做深度体检,输出结构化诊断报告。
诊断流程
1. 定位组件 → 读取全部源文件(含子目录)。真正的缺陷常藏在子文件(hook/util)里,跳过任何文件就会漏掉
2. 概括职责 → 用一句话说清这个组件做什么
3. 逐维度评分 → 回答核心问题 + 打分 + 找代码证据
4. 交叉检查 → 回看每个维度,补齐遗漏的具体问题
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 行
- 同一个概念在不同地方叫不同名字
- 文件之间循环 import7. 边界与契约(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` 强制转型,原因不明
- 不同调用路径对同一数据的校验不一致报告格式
按这个骨架输出,按组件规模裁剪。完整字段说明和示例见 references/output-template.md,定稿前对照一遍。
# 组件诊断报告:{组件名}
> {一句话概括组件职责}
## 评分卡 —— 7 维 + 综合,★ 评分 + 一句话评价
## 逐维度分析 —— 每维优点/不足,引用 文件名:行号,说清"是什么 + 为什么是问题"
## 亮点 —— 2-3 个值得推广的具体设计,引用代码位置
## 改进建议 —— 按 P0(尽快改) / P1(建议优化) / P2(锦上添花) 分级,
每条含 问题 / 影响 / 建议(给方向不写代码) / 工作量(小<1h / 中1-4h / 大>4h)
## 架构图(可选)—— 多层结构时用文本图或 mermaid 画数据流诊断原则
- 证据驱动:每个评分都要有具体代码位置支撑,不凭印象打分。引用格式:
文件名:行号或文件名中的函数名 - 深入代码:表面的结构夸奖("分层清晰"、"命名不错")任何人扫一眼目录就能得出,价值有限。真正有用的诊断来自逐行阅读关键路径后才能发现的问题,重点查这几类:
- 追踪数据在每一层的具体变换,看变换链是否清晰单向
- 检查 useMemo/useEffect 的依赖列表是否真的稳定(漏依赖、引用每次都变)
- 追踪类型断言(
as、as never、as any)出现的位置和背后的设计妥协 - 识别每帧/每次渲染中不必要的重复计算
- 检查 cleanup 函数是否真正清理了异步操作,而不只是设个 flag
- 上下文感知:一个 50 行的工具组件和一个 2000 行的复合组件,标准不同。简单组件不需要四层流水线,复杂组件没有测试才是问题
- 不重复计分:同一个问题只在最相关的维度扣分。重复计分会拉偏综合分,让一个根因看起来像好几个独立问题
- 建议可执行:改进建议要具体到"怎么改",而不是"应该更好"
- 先褒后贬:先说亮点,再说问题。好的设计值得被看见
- 扣分要有底气,给高分也要有证据:找到了具体代码问题才扣分,不是"感觉可以更好"就扣;给 5 分也不能因为"没发现问题"就默认——5 分意味着在这个维度上找到了值得学习的设计模式
什么时候用别的 skill
这个 skill 聚焦单个 React 组件的 7 维深度诊断。如果用户真正要的是别的,先转过去:
- 整个代码库 / 模块边界 / 抽象深浅 / 依赖方向(不是单组件)→
hai-architecture(基于 APoSD 的系统级架构审查)或improve-codebase-architecture(全仓找重构机会) - 只想消除
any/ 收紧类型 →ts-type-safety-reviewer - 通用代码坏味道 / Clean Code 体检(命名、函数过长、重复、过度设计、魔法数字)→
clean-code-reviewer - 只想把最近改动的代码简化整理一下 →
code-simplifier
判定信号:用户指向单个组件目录/.tsx 并问设计质量 → 留在这里;范围一旦扩到多模块、跨文件架构或全仓重构 → 转 architecture 类 skill。