1. 前言
- useMemo 和 useCallback ,React.memo是react中 性能优化用的最多的,乱用的也是最多的
- 简单写下自己的拙见吧 记录下,方便以后进步了打脸哈哈
2. 是什么 what
- useMemo、useCallback 和 React.memo 都是 React 中
性能优化
的工具,它们可以用来避免不必要
的重新计算和渲染
,提高组件的性能。
3. useMemo
- useMemo 是一个用于
性能优化
的 Hook,它接受一个计算函数
和一个依赖数组
作为参数。- 计算函数会在依赖项发生
变化
时执行
,并返回计算结果。- useMemo 会在组件渲染过程中进行
记忆
,并在依赖项未
发生变化
时,直接返回上一次的计算结果,避免重复计算。- 这对于一些计算量较大的操作或者昂贵的函数调用非常有用
import React, { useMemo } from 'react';
function MyComponent({ a, b }) {
const result = useMemo(() => {
// 计算逻辑
return a + b;
}, [a, b]);
return <div>{result}</div>;
}
export default MyComponent;
- useMemo 会根据依赖项 a 和 b 的变化来决定是否重新计算 result。
- 当 a 或 b 发生变化时,useMemo 会执行计算函数并返回新的结果
- 否则它会直接返回上一次的计算结果
- vue3 也有个 v-memo指令哦
4. useCallback
- useCallback 也是一个用于
性能优化
的 Hook,它用于返回一个记忆化的回调函数
。- 它接受一个回调函数和一个依赖数组作为参数,并在依赖项未发生变化时,返回上一次的回调函数。
- 这样可以避免在每次渲染时创建新的回调函数,减少不必要的函数创建和传递
import React, { useCallback } from 'react';
function MyComponent({ onClick }) {
const handleClick = useCallback(() => {
// 处理点击事件
onClick();
}, [onClick]);
return <button onClick={handleClick}>Click</button>;
}
export default MyComponent;
- useCallback 会根据依赖项 onClick 的变化来决定是否返回新的回调函数。
- 当 onClick 发生变化时,useCallback 返回一个新的回调函数,否则它会直接返回上一次的回调函数。
5. React.memo
- React.memo 是一个
高阶组件
,用于对函数组件进行记忆化处理。- 它接受一个组件作为参数,并返回一个经过记忆化处理的新组件。
- 记忆化组件会在其 props 发生变化时进行浅比较,如果 props 没有发生变化,就会直接使用上一次的渲染结果,避免不必要的重新渲染。
import React from 'react';
const MyComponent = React.memo(({ value }) => {
// 组件渲染逻辑
return <div>{value}</div>;
});
export default MyComponent;
React.memo 会根据组件的 props 进行
浅比较
,如果 props 没有发生变化,就会直接使用上一次的渲染结果,避免重新渲染。
6. 综合对比demo
import React, { useState, useMemo, useCallback } from 'react';
function ParentComponent() {
const [count, setCount] = useState(0);
const doubleCount = useMemo(() => {
console.log('Calculating double count...');
return count * 2;
}, [count]);
const handleClick = useCallback(() => {
console.log('Button clicked');
setCount(count + 1);
}, [count]);
return (
<div>
<ChildComponent value={doubleCount} onClick={handleClick} />
</div>
);
}
const ChildComponent = React.memo(({ value, onClick }) => {
console.log('Rendering child component...');
return (
<div>
<p>Value: {value}</p>
<button onClick={onClick}>Click</button>
</div>
);
});
export default ParentComponent;
ParentComponent
组件使用了useMemo
缓存了 doubleCount 的计算结果
- 并使用
useCallback
缓存了 handleClick 的回调函数
。
ChildComponent
组件使用了React.memo
进行了记忆化处理,当 ParentComponent 的 count 发生变化时,只有 doubleCount 发生变化时,ChildComponent 才会重新渲染。这样可以有效避免不必要的计算和渲染
7. 列表对比
import React, { useState, useMemo, useCallback } from 'react';
// 假设有一个大型数据源
const dataSource = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' },
// ...更多项
];
function ListComponent() {
const [selectedItem, setSelectedItem] = useState(null);
const handleItemClick = useCallback((item) => {
setSelectedItem(item);
}, []);
const renderedItems = useMemo(() => {
console.log('Calculating rendered items...');
return dataSource.map((item) => (
<Item
key={item.id}
item={item}
onClick={handleItemClick}
isSelected={selectedItem === item}
/>
));
}, [handleItemClick, selectedItem]);
return <div>{renderedItems}</div>;
}
const Item = React.memo(({ item, onClick, isSelected }) => {
console.log('Rendering item...');
return (
<div
onClick={() => onClick(item)}
style={{ background: isSelected ? 'yellow' : 'white' }}
>
{item.name}
</div>
);
});
export default ListComponent;
8. 咋知道组件是否需要缓存呢
成本
- 工作量的增加: 一旦使用缓存,就必须保证组件本身以及所有 props 都缓存,后续添加的所有 props 都要缓存。
- 代码复杂度和可读性的变化:代码中出现大量缓存函数,这会增加代码复杂度,并降低易读性
- 性能成本: 组件的缓存是在初始化时进行,虽然每个组件缓存的性能耗费很低,通常不足1ms,但大型程序里成百上千的组件如果同时初始化缓存,成本可能会变得很可观
- 所以局部使用 memo,比全局使用显的更优雅、性能更好,坏处是需要开发者主动去判断是否需要缓存该子组件。
这里也有建议官方直接加上默认缓存组件的,论坛里各种谈论都有
判断依据
- 人肉判断,开发或者测试人员在研发过程中感知到渲染性能问题,并进行判断。
- 通过工具协助开发者在查看组件性能
9. 总结
1.使用 React.memo:使用 React.memo 对组件进行记忆化处理,避免不必要的重新
渲染
。
- 使用 useMemo:使用 useMemo 缓存
计算
昂贵的值,避免重复计算。
- 使用 useCallback:使用 useCallback 缓存
回调函数
,避免在每次渲染时创建新的回调函数。