React应用在复杂场景下容易出现渲染性能瓶颈,合理优化能显著提升用户体验。React性能优化手段的核心在于减少不必要的渲染、控制资源加载和合理使用缓存机制。
1. 使用 React.memo 避免子组件无意义重渲染
当父组件更新时,即使子组件props未变,也会默认重新渲染。React.memo可缓存组件输出,仅在props变化时重新更新。
示例Demo:
1import React, { useState } from "react"; 2 3const ExpensiveComponent = React.memo(({ count }) => { 4 console.log("ExpensiveComponent 渲染了"); 5 return <div>计算结果: {count * 100}</div>; 6}); 7 8function App() { 9 const [count, setCount] = useState(0); 10 const [toggle, setToggle] = useState(false); 11 12 return ( 13 <div style={{ padding: "20px", fontFamily: "sans-serif" }}> 14 <h2>React.memo 避免子组件重渲染</h2> 15 <button onClick={() => setCount((c) => c + 1)}>增加计数</button> 16 <button onClick={() => setToggle((t) => !t)}>切换状态</button> 17 <p>当前计数: {count}</p> 18 <ExpensiveComponent count={count} /> 19 <p style={{ marginTop: "16px" }}> 20 切换状态不会触发 ExpensiveComponent 重渲染 21 </p> 22 </div> 23 ); 24} 25 26export default App; 27

附上codesandbox链接:react-memo
关键点:React.memo默认做浅比较,适用于props为基本类型或不变对象的场景。
2. 使用 useMemo 缓存昂贵计算
对复杂计算(如过滤、排序、数学运算)使用useMemo避免每次渲染都重新执行。
示例Demo:
1import React, { useState, useMemo } from "react"; 2 3function ExpensivePrimeCounter() { 4 const [limit, setLimit] = useState(0); 5 const [toggle, setToggle] = useState(false); 6 7 // 模拟昂贵计算:计算小于 limit 的质数个数 8 const primeCount = useMemo(() => { 9 console.log("✅ 重新计算质数个数(未被缓存)"); 10 let count = 0; 11 for (let i = 2; i <= limit; i++) { 12 let isPrime = true; 13 for (let j = 2; j * j <= i; j++) { 14 if (i % j === 0) { 15 isPrime = false; 16 break; 17 } 18 } 19 if (isPrime) count++; 20 } 21 return count; 22 }, [limit]); // 仅当 limit 变化时重新计算 23 24 return ( 25 <div 26 style={{ 27 padding: "2rem", 28 fontFamily: "sans-serif", 29 maxWidth: "500px", 30 margin: "0 auto", 31 }} 32 > 33 <h2>质数计数器</h2> 34 <p> 35 当前上限:<strong>{limit}</strong> 36 </p> 37 <p> 38 小于 {limit} 的质数个数:<strong>{primeCount}</strong> 39 </p> 40 <button 41 onClick={() => setLimit((c) => c + 1)} 42 style={{ margin: "0.5rem" }} 43 > 44 增加上限 45 </button> 46 <button onClick={() => setToggle((t) => !t)}>切换状态</button> 47 <p style={{ fontSize: "0.8rem", color: "#666" }}> 48 注意:仅当上限变化时,质数计算才会重新执行 —— 缓存生效! 49 </p> 50 </div> 51 ); 52} 53 54export default ExpensivePrimeCounter; 55

附上codesandbox链接:react-useMemo
注意:不要滥用useMemo,仅对计算成本高的场景使用(如1000+条数据处理)
3. 使用 useCallback 避免回调函数重新创建
回调函数作为props传递给子组件时,每次渲染都会生成新函数,导致React.memo失效。
示例Demo:
1import React from "react"; 2 3const Button = React.memo(({ onClick, label }) => { 4 console.log("Button rendered"); 5 return <button onClick={onClick}>{label}</button>; 6}); 7 8export default function Parent() { 9 const [count, setCount] = React.useState(0); 10 11 // ❌ 每次渲染都创建新函数 → Button 会重新渲染 12 // const handleClick = () => setCount(c => c + 1); 13 14 // ✅ 使用 useCallback 缓存函数引用 15 const handleClick = React.useCallback(() => { 16 setCount((c) => c + 1); 17 }, []); 18 19 return ( 20 <div> 21 <p>Count: {count}</p> 22 <Button label="Increment" onClick={handleClick} /> 23 </div> 24 ); 25} 26
附上codesandbox链接:react-useCallback
useCallback与React.memo配合使用,是优化组件树的关键组合。
4. 虚拟滚动:渲染大量列表时只渲染可见项
对长列表(如1000+项)使用虚拟滚动,避免DOM节点爆炸。
1import React from 'react'; 2import { FixedSizeList as List } from 'react-window'; 3 4const Row = ({ index, style }) => ( 5 <div style={style}> 6 Item {index} 7 </div> 8); 9 10function LongList({ items }) { 11 return ( 12 <List 13 height={400} 14 itemCount={items.length} 15 itemSize={35} 16 width="100%" 17 > 18 {Row} 19 </List> 20 ); 21} 22
需安装依赖:react-window或react-virtualized
性能提升:从渲染1000个DOM节点 → 仅渲染10~20个可见节点
5. 懒加载组件:按需加载非关键模块
使用React.lazy + Suspense实现代码分割,减少首屏加载体积。
1import React, { lazy, Suspense } from 'react'; 2 3const HeavyComponent = lazy(() => import('./HeavyComponent')); 4 5function App() { 6 return ( 7 <div> 8 <h1>Dashboard</h1> 9 <Suspense fallback={<div>Loading...</div>}> 10 <HeavyComponent /> 11 </Suspense> 12 </div> 13 ); 14} 15
适用于:弹窗、配置页、图标组件等非首屏内容
可结合webpack实现自动代码分割,SSR模式下可以使用@loadable/component
6. 使用React DevTools分析渲染性能
安装 React DevTools 浏览器插件,启用“Highlight updates when components render”功能,直观看到哪些组件被不必要地重渲染。
总结
| 优化手段 | 适用场景 | 是否推荐 |
|---|---|---|
| React.memo | 子组件props稳定 | 强烈推荐 |
| useMemo | 复杂计算、昂贵函数 | 推荐 |
| useCallback | 回调传给memoized子组件 | 推荐 |
| 虚拟滚动 | 长列表(>500项) | 必须使用 |
| React.lazy/@loadable/component | 非首屏组件 | 推荐 |
不要过早优化。先保证功能正确,再用工具定位瓶颈,针对性优化。