1. Reactperformanceoptimizationoverview
React is a high performance before 端framework, 但 in processing complex application时, 仍然需要forperformanceoptimization. performanceoptimization可以分 for many 个层面, includingcomponentoptimization, 渲染optimization, statusmanagementoptimizationetc..
1.1 performanceoptimization 目标
- reducing不必要 重 new 渲染
- optimization渲染速度
- reducingmemory占用
- improvingapplicationresponse速度
- optimizationresource加载
2. componentoptimization
componentoptimization is Reactperformanceoptimization Basics, 可以through many 种方式reducingcomponent 重 new 渲染.
2.1 usingReact.memo
React.memo is a high 阶component, 用于cachecomponent 渲染结果, 只 has 当component props发生变化时才会重 new 渲染.
const MemoizedComponent = React.memo(Component);
// usingexample
const ExpensiveComponent = React.memo(({ data }) => {
console.log('ExpensiveComponent rendered');
// 执行昂贵 计算
return <div>{data}</div>;
});
function ParentComponent() {
const [count, setCount] = useState(0);
const [data, setData] = useState('test');
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<ExpensiveComponent data={data} />
</div>
);
}
提示
React.memo默认using浅比较来比较props, such as果props is complex object, 需要providing自定义比较function.
2.2 usinguseMemo and useCallback
useMemo用于cache计算结果, useCallback用于cachefunction引用, 避免不必要 重 new 计算 and functioncreation.
// useMemoexample
function ExpensiveCalculation() {
const [count, setCount] = useState(0);
const [number, setNumber] = useState(10);
const expensiveResult = useMemo(() => {
console.log('Calculating expensive result...');
let result = 0;
for (let i = 0; i < 100000000; i++) {
result += i;
}
return result + number;
}, [number]);
return (
<div>
<p>Count: {count}</p>
<p>Expensive Result: {expensiveResult}</p>
<button onClick={() => setCount(count + 1)}>Increment Count</button>
<button onClick={() => setNumber(number + 1)}>Increment Number</button>
</div>
);
}
// useCallbackexample
const MemoizedChild = React.memo(({ onButtonClick }) => {
console.log('MemoizedChild rendered');
return <button onClick={onButtonClick}>Click me</button>;
});
function ParentComponent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('Button clicked');
}, []);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<MemoizedChild onButtonClick={handleClick} />
</div>
);
}
2.3 usingshouldComponentUpdate
for 于classcomponent, 可以usingshouldComponentUpdate生命周期method来控制component is 否需要重 new 渲染.
class CustomComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
// 只 has 当props.id or state.count发生变化时才重 new 渲染
return nextProps.id !== this.props.id || nextState.count !== this.state.count;
}
render() {
return <div>{this.props.id} - {this.state.count}</div>;
}
}
2.4 usingPureComponent
PureComponent is Reactproviding 一个基class, 它implementation了shouldComponentUpdatemethod, using浅比较来比较props and state.
class PureCustomComponent extends React.PureComponent {
render() {
return <div>{this.props.data}</div>;
}
}
3. 渲染optimization
渲染optimization可以reducingDOMoperation, improving渲染速度.
3.1 usingkeyproperty
in 渲染list时, using stable keyproperty可以helpingReact识别哪些元素发生了变化, 避免不必要 DOMoperation.
// error 做法
const list = items.map((item, index) => (
<Item key={index} data={item} />
));
// 正确 做法
const list = items.map((item) => (
<Item key={item.id} data={item} />
));
3.2 避免 in 联样式 and in 联function
in 联样式 and in 联function会导致component每次渲染时creation new 引用, 影响React diffalgorithms.
// error 做法
function Component() {
return (
<div style={{ color: 'red' }} onClick={() => console.log('clicked')}>
Click me
</div>
);
}
// 正确 做法
const styles = { color: 'red' };
function Component() {
const handleClick = useCallback(() => {
console.log('clicked');
}, []);
return (
<div style={styles} onClick={handleClick}>
Click me
</div>
);
}
3.3 usingvirtualizationlist
for 于 long list, 可以usingvirtualizationtechniques只渲染可见区域 元素, reducingDOMnode数量.
// usingreact-windowlibrary
import { FixedSizeList as List } from 'react-window';
function VirtualizedList({ items }) {
const Row = ({ index, style }) => {
return (
<div style={style}>
{items[index]}
</div>
);
};
return (
<List
height={400}
itemCount={items.length}
itemSize={35}
width="100%"
>
{Row}
</List>
);
}
4. statusmanagementoptimization
statusmanagementoptimization可以reducing不必要 statusupdate and 重 new 渲染.
4.1 避免status提升过 high
status提升过 high 会导致更 many component 重 new 渲染, 应该将status尽可能地保留 in 需要它 componentin.
4.2 using不可变data
using不可变data可以helpingReact更 high 效地检测status变化, 避免不必要 重 new 渲染.
// error 做法
state.items.push(newItem);
setState(state);
// 正确 做法
setState(prevState => ({
...prevState,
items: [...prevState.items, newItem]
}));
// usingImmerlibrary
import produce from 'immer';
setState(produce(draft => {
draft.items.push(newItem);
}));
4.3 批量updatestatus
React会批量processingstatusupdate, 避免 many 次重 new 渲染.
// React会将这两次setStatemerge for 一次update
setState({ count: count + 1 });
setState({ name: 'new name' });
// in asynchronousfunctionin, 需要usinguseState function形式来确保获取最 new status
setTimeout(() => {
setState(prevState => ({ count: prevState.count + 1 }));
setState(prevState => ({ count: prevState.count + 1 }));
}, 0);
5. resourceoptimization
resourceoptimization可以reducingapplication 加载时间 and run时resource占用.
5.1 code分割
usingcode分割可以将applicationcode拆分 for many 个bundle, 按需加载, reducing初始加载时间.
// usingReact.lazy and Suspense
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</div>
);
}
// usingReact Routerforrouting级别 code分割
const Home = React.lazy(() => import('./Home'));
const About = React.lazy(() => import('./About'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
5.2 懒加载graph片
懒加载graph片可以reducing初始加载时间, 只 in graph片进入视口时才加载.
// usingloading="lazy"property
<img src="large-image.jpg" loading="lazy" alt="Large image" />
// usingIntersection Observer API
function LazyImage({ src, alt }) {
const imgRef = useRef(null);
const [isLoaded, setIsLoaded] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
setIsLoaded(true);
observer.disconnect();
}
},
{ threshold: 0.1 }
);
if (imgRef.current) {
observer.observe(imgRef.current);
}
return () => {
if (imgRef.current) {
observer.unobserve(imgRef.current);
}
};
}, []);
return (
<img
ref={imgRef}
src={isLoaded ? src : ''}
alt={alt}
style={{ minHeight: '200px' }}
/>
);
}
6. performancemonitor and debug
usingperformancemonitortool可以helping我们识别performance瓶颈, for针 for 性optimization.
6.1 usingReact DevTools
React DevTools is React官方providing debugtool, 可以查看componenttree, checkcomponent props and state, analysiscomponent 渲染performance.
6.2 usingPerformance API
using浏览器 Performance API可以测量component 渲染时间, 识别performance瓶颈.
function PerformanceMonitor({ children }) {
const start = useRef(null);
useEffect(() => {
start.current = performance.now();
}, []);
useEffect(() => {
if (start.current) {
const end = performance.now();
console.log(`Component rendered in ${end - start.current}ms`);
}
});
return children;
}
// usingexample
<PerformanceMonitor>
<ExpensiveComponent />
</PerformanceMonitor>
6.3 usingProfilercomponent
React Profiler is React 16.5引入 performanceanalysistool, 可以测量component 渲染时间 and 次数.
<Profiler id="App" onRender={(id, phase, actualDuration, baseDuration, startTime, submittingTime) => {
console.log({
id,
phase,
actualDuration,
baseDuration,
startTime,
submittingTime
});
}}>
<App />
</Profiler>
7. best practices
- 优先usingfunctioncomponent and Hooks
- usingReact.memo, useMemo and useCallbackoptimizationcomponent
- 避免不必要 statusupdate and 重 new 渲染
- using不可变data
- optimization long list, usingvirtualizationtechniques
- usingcode分割 and 懒加载
- optimizationgraph片加载
- 定期monitor and analysisperformance
练习 1: usingReact.memooptimizationcomponent
- creation一个昂贵 component, package含 complex 计算
- usingReact.memocache该component
- verificationcomponent只 in props变化时才重 new 渲染
练习 2: usinguseMemooptimization计算
- creation一个package含昂贵计算 component
- usinguseMemocache计算结果
- verification计算只 in 依赖项变化时才重 new 执行