React中的hook之useMemo
1.useMemo介绍
众所周知,react中的useMemo
和useCallback
与组件的性能优化有关,useMemo
与状态hookuseState
不同,需要着重的介绍其实现逻辑,理解之后才能用在更加合适的地方,即-最佳实践.其性能优化主要在下面这几方面:
- 跳过代价昂贵的重新计算
- 跳过组件的重新渲染
- 记忆另一个 Hook 的依赖
2.何时使用useMemo
react项目中,所有需要性能优化的地方,当开发大型项目时,在不过高提升开发者心智负担的同时,尽可能多使用useMemo来进行饱和时开发..
3.useMemo使用方法
const cachedValue = useMemo(calculateValue, dependencies)
当我们使用useMemo时,接受两个参数:
1.要缓存计算值的函数 纯函数,返回结果为cachedValue
2.函数计算的依赖值 数组,组件每次渲染时,检查dependencies是否变化,若无变化,则不计算
举例说明:
(1).跳过代价昂贵的重新计算:
在前后端分离的开发模式下,我们一般认为数据计算,分析应该交给服务端,我们只需要调取api获取数据来完成组件的渲染,但某些特殊情况下,通过客户端的计算来达到开发目的,比通过服务端计算得到数据更加快速,安全,用户体验更好,所以现在讨论通过客户端计算的例子:
比如count
是从服务端获取的数据,我们需要除掉数组中不是x
倍数的数字,很轻松可以完成
//count :[1,2,3,4,5,6,7,8,9,10]
console.log(count.filter((item) => item % x === 0))
但是当count
的length
很大,客户端的计算将很昂贵,例如:
import { useState } from "react";
let count = [];
const Test = () => {
const [data, setData] = useState(1);
for (let i = 0; i < 10000000; i++) {
count.push(i);
}
function handChange() {
setData(2);
}
console.log(count);
return (
<div onClick={handChange}>{data}</div>
);
};
export default Test;
当我们点击屏幕时,触发handChange方法,更改data,页面重新渲染,将重新执行for循环来更新count数组,此时能明显的感受到,页面加载极慢,此时useMemo
就可以发挥出他的作用:
import { useMemo, useState } from "react";
let count = [];
const Test = () => {
const [data, setData] = useState(1);
count = useMemo(() => {
let newCount = [];
for (let i = 0; i < 10000000; i++) {
newCount.push(i);
}
return newCount;
}, []);
console.log(count);
function handChange() {
setData(2);
}
return <div onClick={handChange}>{data}</div>;
};
export default Test;
当我们查看count的打印时间,可以明显的体现···
(2).跳过组件的重新渲染:
与跳过代价昂贵的重新计算不同,页面渲染是发生在js计算之后的..
所有学过react的同学都知道,react的性能较强得益于他自身的虚拟DOM(diff算法)
:
当组件重新渲染时,会从当前的根目录开始做深度优先遍历
,生成虚拟DOM
,两个虚拟DOM
相比较,如果没有改变,则不重新生成DOM
树,否则更新虚拟DOM
树并更新真实DOM
..
而useMemo
的作用是直接跳过渲染,当外层组件重新渲染时,深度遍历到useMemo
作用下的组件时,直接跳过,不进行后续过程(diff算法)
的计算..
以下方代码为例,每次点击页面,data
值增加1,并且会打印出count
,因为组件Card
每次都会重新渲染,触发js执行,再生成虚拟DOM
,比较后发现虚拟DOM
相同,不再渲染页面
import { useState } from "react";
const Test = () => {
const [data, setData] = useState(1);
const count = { name: "小明", age: 18 };
function handChange() {
setData(() => data + 1);
}
return (
<div onClick={handChange}>
{data}
<Card count={count} />
</div>
);
};
export default Test;
const Card = ({ count }) => {
console.log(count);
return <div>{count.name}</div>;
};
而useMemo提供了更好的交互方式,当组件得知,定义的依赖没有发生变化,表示页面操作与自身组件没有关系时,不再生产虚拟DOM
从组件根目录对比 ,而是直接跳过,例子如下
import { memo, useMemo, useState } from "react";
const Test = ({text}) => {
const [data, setData] = useState(1);
const count = useMemo(() => {
return { name: "小明", age: 18 };
}, [text]);
function handChange() {
setData(() => data + 1);
}
return (
<div onClick={handChange}>
{data}
<Card count={count} />
</div>
);
};
export default Test;
const Card = memo(({ count }) => {
console.log(count);
return <div>{count.name}</div>;
});
(3).记忆另一个 Hook 的依赖:
useMemo
的依赖,一般为父组件传来的props
,当props
发生变化,才会重新渲染,但如果我们不仅仅想使用props
,则在useMemo
定义依赖,否则每次组件重新渲染,定义的引用数据类型将重新创建并赋予新的指针,依赖将自动改变,代码如下:
import { memo, useMemo, useState } from "react";
const Test = ({text}) => {
const [data, setData] = useState(1);
//const person={sex:'男',text:text } //错误定义依赖,Test渲染时person改变,每次点击都将打印
const person = useMemo(() => {
return { sex: '男', text }; },
[text]); //正确定义依赖,记忆另一个useMemo的依赖
const count = useMemo(() => {
return { name: "小明", age: 18 };
}, [person]);
function handChange() {
setData(() => data + 1);
}
return (
<div onClick={handChange}>
{data}
<Card count={count} />
</div>
);
};
export default Test;
const Card = memo(({ count }) => {
console.log(count);
return <div>{count.name}</div>;
});
4.useMemo总结
useMemo
与其他hook
不同,不属于业务hook
,属于优化hook
,当我们整个react
项目都不使用useMemo
,也不会影响正常的组件运行,反而,如果不当的使用,不仅仅会增加开发者的心智负担,也可能使项目的优化结果不尽人意..
但是,只有掌握了useMemo
的使用时机和使用方法,才能更上一层楼,使得项目以最佳状态运行,学习useMemo
重要的是理解,加油,少年!
转载自:https://juejin.cn/post/7281210797959725067