关于 react-performance
面向优化 React 应用的开发者性能技能,涵盖性能分析、记忆化、虚拟化、代码拆分和状态更新实践,并提供常见问题的排查建议。
主要功能
- 01使用 React API 进行记忆化
- 02长列表虚拟化
- 03路由与组件代码拆分
- 04优化状态更新
- 05性能分析与故障排查
适用场景
- 01减少组件的不必要重渲染
- 02改善大型列表的滚动体验
- 03通过代码拆分缩短初始加载
- 04诊断更新缓慢或包体积过大
MCP 相关
该技能引用 `mcp__documentation__fetch_docs` 获取 React 性能文档。
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ claude-dev-suite/ claude-dev-suite --skill react-performance安装后在 Claude Code 中输入/react-performance确认已加载。
附带文件advanced.md
详细介绍
React 性能优化
深入了解:使用
mcp__documentation__fetch_docs,指定 technology:react、topic:performance,获取 React 性能优化和性能分析技术的全面文档。
完整参考:请参阅 advanced.md,了解使用 React DevTools 进行性能分析、包优化、图像优化和 Web Workers。
记忆化
React.memo
当 props 未发生变化时,阻止组件重新渲染:
// Only re-renders when props change (shallow comparison)
const ExpensiveComponent = memo(function ExpensiveComponent({
data,
onItemClick,
}: Props) {
return (
<ul>
{data.map(item => (
<li key={item.id} onClick={() => onItemClick(item.id)}>
{item.name}
</li>
))}
</ul>
);
});
// With custom comparison
const OptimizedComponent = memo(
function OptimizedComponent({ user }: Props) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => prevProps.user.id === nextProps.user.id
);
何时使用 memo
// ✅ Good: Expensive component with stable parent
const ExpensiveList = memo(function ExpensiveList({ items }: { items: Item[] }) {
return items.map(item => <ExpensiveItem key={item.id} item={item} />);
});
// ❌ Bad: Simple component, memo overhead not worth it
const SimpleText = memo(function SimpleText({ text }: { text: string }) {
return <span>{text}</span>;
});
// ❌ Bad: Props always change anyway
function Parent() {
// New object on every render - memo is useless!
return <MemoizedChild data={{ value: 1 }} />;
}
useMemo
对开销较大的计算进行记忆化:
function ProductList({ products, filter }: Props) {
const filteredProducts = useMemo(() => {
return products
.filter(p => p.category === filter.category)
.filter(p => p.price >= filter.minPrice && p.price <= filter.maxPrice)
.sort((a, b) => a.name.localeCompare(b.name));
}, [products, filter.category, filter.minPrice, filter.maxPrice]);
return (
<ul>
{filteredProducts.map(p => <ProductCard key={p.id} product={p} />)}
</ul>
);
}
// ❌ Don't overuse - simple operations don't need memoization
const total = useMemo(() => a + b, [a, b]); // Overkill!
useCallback
对函数进行记忆化,以避免子组件重新渲染:
function Parent() {
const [count, setCount] = useState(0);
// ✅ Stable reference for child components
const handleClick = useCallback((id: string) => {
console.log('Clicked:', id);
}, []);
// ✅ Dependencies that change function behavior
const handleSubmit = useCallback((data: FormData) => {
submitWithCount(data, count);
}, [count]);
return (
<>
<ChildComponent onClick={handleClick} />
<Form onSubmit={handleSubmit} />
</>
);
}
虚拟化
对于大型列表,仅渲染可见项:
import { FixedSizeList } from 'react-window';
function VirtualList({ items }: { items: Item[] }) {
const Row = ({ index, style }: { index: number; style: CSSProperties }) => (
<div style={style} className="list-item">
{items[index].name}
</div>
);
return (
<FixedSizeList
height={400}
width="100%"
itemCount={items.length}
itemSize={50}
>
{Row}
</FixedSizeList>
);
}
使用 TanStack Virtual
import { useVirtualizer } from '@tanstack/react-virtual';
function VirtualList({ items }: { items: Item[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
overscan: 5,
});
return (
<div ref={parentRef} style={{ height: 400, overflow: 'auto' }}>
<div style={{ height: virtualizer.getTotalSize(), position: 'relative' }}>
{virtualizer.getVirtualItems().map((virtualItem) => (
<div
key={virtualItem.key}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: virtualItem.size,
transform: `translateY(${virtualItem.start}px)`,
}}
>
{items[virtualItem.index].name}
</div>
))}
</div>
</div>
);
}
代码拆分
基于路由的拆分
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./pages/Home'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
function App() {
return (
<Suspense fallback={<PageLoader />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}
基于组件的拆分
const HeavyChart = lazy(() => import('./HeavyChart'));
function Dashboard() {
const [showChart, setShowChart] = useState(false);
return (
<div>
<button onClick={() => setShowChart(true)}>Show Chart</button>
{showChart && (
<Suspense fallback={<ChartSkeleton />}>
<HeavyChart data={data} />
</Suspense>
)}
</div>
);
}
预加载
const Dashboard = lazy(() => import('./Dashboard'));
const preloadDashboard = () => import('./Dashboard');
function NavLink() {
return (
<Link
to="/dashboard"
onMouseEnter={preloadDashboard}
onFocus={preloadDashboard}
>
Dashboard
</Link>
);
}
避免不必要的重新渲染
稳定引用
// ❌ Bad: New object on every render
function Parent() {
return <Child style={{ color: 'red' }} />;
}
// ✅ Good: Stable reference
const style = { color: 'red' };
function Parent() {
return <Child style={style} />;
}
// ✅ Good: useMemo for dynamic values
function Parent({ color }) {
const style = useMemo(() => ({ color }), [color]);
return <Child style={style} />;
}
组件组合
// ❌ Bad: Entire component re-renders on count change
function App() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>{count}</button>
<ExpensiveComponent /> {/* Re-renders on every count change! */}
</div>
);
}
// ✅ Good: Move state down
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
function App() {
return (
<div>
<Counter />
<ExpensiveComponent /> {/* Doesn't re-render! */}
</div>
);
}
// ✅ Good: Pass children as props
function Counter({ children }) {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>{count}</button>
{children} {/* Doesn't re-render! */}
</div>
);
}
状态管理优化
批量更新
// React 18+ automatically batches these
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
setText('updated');
// Only ONE re-render!
}
派生状态
// ❌ Bad: Synchronized state
function Form() {
const [items, setItems] = useState([]);
const [total, setTotal] = useState(0);
useEffect(() => {
setTotal(items.reduce((sum, item) => sum + item.price, 0));
}, [items]); // Extra re-render!
}
// ✅ Good: Calculate during render
function Form() {
const [items, setItems] = useState([]);
const total = items.reduce((sum, item) => sum + item.price, 0);
}
// ✅ Good: useMemo for expensive calculations
function Form() {
const [items, setItems] = useState([]);
const total = useMemo(
() => items.reduce((sum, item) => sum + item.price, 0),
[items]
);
}
常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 初始渲染缓慢 | 包体积过大 | 代码拆分、延迟加载 |
| 更新缓慢 | 重新渲染次数过多 | memo、useMemo、useCallback |
| 滚动卡顿 | 渲染了列表中的所有项 | 虚拟化 |
| 内存泄漏 | 未清理副作用 | 正确的清理函数 |
| 布局抖动 | 强制同步布局 | 批量读取/写入 DOM |
最佳实践
- ✅ 优化前先进行性能分析
- ✅ 使用 React DevTools Profiler
- ✅ 对长列表(>100 项)进行虚拟化
- ✅ 在路由层级进行代码拆分
- ✅ 对开销较大的计算进行记忆化
- ✅ 为对象/函数使用稳定引用
- ❌ 不要过早进行过度优化
- ❌ 不要给所有内容都包上 memo
- ❌ 不要让繁重的 JavaScript 阻塞主线程
何时不应使用此技能
- React 19 编译器优化 - 使用
react-19技能了解编译器专属功能 - React 基础开发 - 使用
react技能进行常规组件开发 - 测试 - 使用
react-testing技能了解性能测试策略 - 非性能相关的 React 问题 - 此技能专用于优化
反模式
| 反模式 | 问题 | 解决方案 |
|---|---|---|
| 过早优化 | 浪费精力,代码复杂 | 先进行性能分析,优化关键部分 |
| 给所有内容都包上 memo | 增加开销,没有收益 | 只对开销较大的组件进行记忆化 |
| 对低成本计算使用 useMemo | 开销大于收益 | 只对开销较大的操作进行记忆化 |
| 在 JSX 中使用内联对象/函数 | 破坏子组件的记忆化 | 提取为常量或使用 useCallback |
| 未正确使用 key 属性 | 整个列表重新渲染 | 使用稳定且唯一的 ID |
| 渲染整个列表 | 滚动缓慢 | 对超过 100 项的列表使用虚拟化 |
| 包体积大且未进行代码拆分 | 初始加载缓慢 | 按路由拆分 |
快速故障排查
| 问题 | 可能原因 | 修复方法 |
|---|---|---|
| 初始加载缓慢 | 包体积过大 | 拆分代码,延迟加载路由 |
| 滚动卡顿 | 渲染了列表中的所有项 | 使用 react-window 或 TanStack Virtual |
| 频繁发生不必要的重新渲染 | props 的身份标识发生变化 | 使用 useMemo/useCallback 对对象/函数进行记忆化 |
| 组件更新缓慢 | 渲染过程中进行了繁重计算 | 移至 useMemo 或 Web Worker |
| 内存泄漏 | 未清理副作用/订阅 | 在 useEffect 中添加清理函数 |
| JavaScript 负载过大 | 未进行 tree-shaking | 只导入所需内容 |
参考文档
- React Profiler
- 性能优化
- MCP:
mcp__documentation__fetch_docs→ technology:react,topic:performance
评论
支持 Markdown