
关于
诊断缓慢的 React 组件并提供针对性的性能优化建议。
name: react-component-performance description: 诊断慢速 React 组件并建议有针对性的性能修复。 risk: safe source: "Dimillian/Skills (MIT)" date_added: "2026-03-25"
React 组件性能优化
概述
识别渲染热点,隔离昂贵的更新,并应用有针对性的优化而不改变 UI 行为。
何时使用
- 当用户要求分析或改善慢速 React 组件时
- 当需要减少 React UI 中的重新渲染、列表卡顿或昂贵的渲染工作时
工作流
- 重现或描述卡顿现象
- 识别触发重新渲染的原因(状态更新、props 变动、effects)
- 将快速变化的状态与重型子树隔离
- 稳定 props 和处理函数;在有收益的地方进行 memo 化
- 减少昂贵的工作(计算、DOM 大小、列表长度)
- 验证:打开 React DevTools Profiler → 录制交互 → 检查 Flamegraph 中渲染超过 ~16ms 的组件 → 与优化前的基线录制对比
检查清单
- 测量:使用 React DevTools Profiler 或记录渲染;捕获基线
- 找到变动:识别在定时器、滚动、输入或动画上更新的状态
- 拆分:将频繁变化的状态移到子组件;保持重型列表静态
- Memo 化:仅当 props 稳定时用
memo包装叶子行 - 稳定 props:对处理函数和派生值使用
useCallback/useMemo - 避免渲染中的派生工作:预计算,或在 memo 化的辅助函数中计算
- 控制列表大小:对长列表进行窗口化/虚拟化;避免渲染隐藏项
- Keys:确保稳定的 key;当顺序可能变化时避免使用 index
- Effects:验证依赖数组;避免每次渲染都重新运行的 effect
- 样式/布局:注意昂贵的布局抖动或大型 Markdown/diff 渲染
优化模式
隔离频繁变化的状态
将定时器或动画计数器移到子组件中,使父级列表不会在每次 tick 时重新渲染。
// ❌ 之前 – 整个父组件(和列表)每秒重新渲染
function Dashboard({ items }: { items: Item[] }) {
const [tick, setTick] = useState(0);
useEffect(() => {
const id = setInterval(() => setTick(t => t + 1), 1000);
return () => clearInterval(id);
}, []);
return (
<>
<Clock tick={tick} />
<ExpensiveList items={items} /> {/* 每秒重新渲染 */}
</>
);
}
// ✅ 之后 – 只有 <Clock> 重新渲染;列表不受影响
function Clock() {
const [tick, setTick] = useState(0);
useEffect(() => {
const id = setInterval(() => setTick(t => t + 1), 1000);
return () => clearInterval(id);
}, []);
return <span>{tick}s</span>;
}
function Dashboard({ items }: { items: Item[] }) {
return (
<>
<Clock />
<ExpensiveList items={items} />
</>
);
}
用 useCallback + memo 稳定回调
// ❌ 之前 – 每次渲染新的处理函数引用破坏 Row memo
function List({ items }: { items: Item[] }) {
const handleClick = (id: string) => console.log(id);
return items.map(item => <Row key={item.id} item={item} onClick={handleClick} />);
}
// ✅ 之后 – 稳定的处理函数;Row 仅在自身 item 变化时重新渲染
const Row = memo(({ item, onClick }: RowProps) => (
<li onClick={() => onClick(item.id)}>{item.name}</li>
));
function List({ items }: { items: Item[] }) {
const handleClick = useCallback((id: string) => console.log(id), []);
return items.map(item => <Row key={item.id} item={item} onClick={handleClick} />);
}
将派生数据移出渲染
// ❌ 之前 – 每次渲染都重新计算
function Summary({ orders }: { orders: Order[] }) {
const total = orders.reduce((sum, o) => sum + o.amount, 0);
return <p>Total: {total}</p>;
}
// ✅ 之后 – 仅当 orders 变化时重新计算
function Summary({ orders }: { orders: Order[] }) {
const total = useMemo(() => orders.reduce((sum, o) => sum + o.amount, 0), [orders]);
return <p>Total: {total}</p>;
}
其他模式
- 拆分行:将列表行提取为带窄 props 的 memo 化组件
- 延迟重型渲染:懒渲染或折叠昂贵内容直到展开
性能分析验证步骤
- 打开 React DevTools → Profiler 标签
- 点击 Record,执行慢速交互,然后 Stop
- 切换到 Flamegraph 视图;任何标记组件且时间 > ~16ms 的条形是候选项
- 使用 Ranked chart 按自身渲染时间排序,针对最大的问题
- 一次应用一个优化,重新录制,对比渲染次数和持续时间与基线
限制
- 仅在任务明确匹配上述描述的范围时使用此技能
- 不要将输出视为环境特定验证、测试或专家审查的替代品
- 如果缺少必需的输入、权限、安全边界或成功标准,请停下来寻求澄清
兼容工具
Claude CodeCursor
标签
前端开发