前言
大家好 我是橙子,今天给大家带来一个memo
的应用场景,就是下面所看到的笑脸。
它会有 喜 怒 哀 乐 五种状态,我们慢慢的移动进度条,笑脸也会发生变化。
这里是放了5张图片如下图, 用我们熟悉的useState
在vlue
里面设置值,它的值分别对应 0 100 200 300 400
, 那它跟我们的react hooks
的哪个函数相关呢,那就是memo
。
理解memo
memo
它是一个高阶组件,它与React.PureComponent
非常相似,但只适用于函数组件,而不适用class
组件。- 那我们怎么来理解
memo
呢,memo
是memory
的简写,所有可以理解成它是一个记忆组件,可以缓存值,memo
针对的是一个组件的渲染是否重复执行,用来优化函数组件的重复渲染行为。当你传入属性值都不变的情况下不会触发组件。那怎么使用memo
来实现性能优化呢? - 在学习
react
中,性能优化的点主要在于:调用setState
,就会触发组件的重新渲染,无论前后state
是否相同,父组件更新,子组件也会自动更新。 - 在
hooks
出来之后,函数组件中没有shouldComponentUpdate
生命周期,我们无法通过判断前后状态来决定是否更新。useEffect
不再区分mount
update
两个状态,这意味着函数组件的每一次调用都会执行其内部的所有逻辑,那么会带来较大的性能损耗。所以,hooks
中学习memo
来减少重复渲染,实现性能优化。接下来通过这个demo
来帮助理解。
memo的应用 笑脸
1. 看效果图,我们要缓存5个图片要改变的位置,当移动滑动条,每次Change
都会触发MVVM视图的重新渲染, 这将会极大的消耗性能。
- 当我们拖动的时候 ,其实我们每次要拖动100 才会变成下一张图片, 没有必要在这个过程之中,发生组件的重新渲染。
- 我们可以把图片看成我们要缓存的组件,也就是
memo
组件,即使我们的滑动范围超过几百次, 我们的子组件也不会跟随着发生几百次的改变,而是仅仅几次,这是不是有点有趣。
2. 接下来我们将5张图片放在src目录下。 接着写入口文件index.js
。
引入react, react-dom
和样式文件 style.css
(样式文件地址)。
import React, { useState } from 'react';
import ReactDom from 'react-dom';
import './styles.css';
import { FaceComponent } from './demo.js';
function App() {
const [satisfactionLevel, setSatisfactionLevel] = useState(300)
return (
<div className="App">
<input
type="range"
min="0"
max="500"
value={satisfactionLevel}
onChange = {(e)=>{setSatisfactionLevel(+e.target.value)}}
/>
<br/>
<span>{satisfactionLevel}</span>
<br/>
<FaceComponent level={satisfactionLevel} />
</div>
)
}
const rootElement = document.getElementById('root');
ReactDom.render(<App/>, rootElement);
声明了一个变量satisfactionLevel
,表示满意度的等级,我们的表情从不开心到有点开心到很开心 的状态改变,初始值为300
。所有这里的setSatisfactionLevel
作用并不大 因为我们不需要去修改值去做其他的事情,而是只是放一张图片在这里。input type="range"
是H5一个新特性,自定义滑动条。
3. 新建demo.js
文件,引入 react
和 memo
然后在写一个函数式组件,看memo
的应用场景。
//这是一个函数式组件 交给memo 进行高阶组件化,它可以帮我们缓存值
export const FaceComponent = memo((props) => {
//解构level
//props会频繁的发生改变,使用memo 传入第二个参数
const { level } = props;
return (
//盒子
<div className={setSatisfactionClass(level)}></div>
)
}, isSameRange)
这里的isSameRange是什么呢,这是memo的第二个参数,动态的改变类名,比较参数来判断前后状态是否要发生改变。通过第二个参数指定一个自定义的比较函数来比较新旧 props。如果函数返回 true,就会跳过更新,useMemo Hook 允许你通过「记住」上一次计算结果的方式在多次渲染的之间缓存计算结果.
声明一个 isSameRange
方法:
const isSameRange = (prevValue, nextValue) => {
//setSatisfactionClass 设置class 表示要它生成的一个类名
// 5个类名表示不同的位置 类名不变 位置就不变
const prevValueClass = setSatisfactionClass(prevValue.level)
const nextValueClass = setSatisfactionClass(nextValue.level)
return prevValueClass === nextValueClass
}
isSameRange 方法来判断两次 props 有什么不同,memo会帮我们缓存上一个值,当我们接收一个新的值之后,两个值进行比较,相同的话,拒绝修改,直接使用已经缓存的值,不同的话, 则改变。 达到即缓存又更新的能力,这是一个很抽象的函数,也会让我们对memo有更深的理解。
4. 写业务函数 setSatisfactionClass
const setSatisfactionClass = (level) => {
//结合css理解一下
//0-99只需要发生一次改变
if (level < 100) {
return "very-dissatisfied"
}
if (level < 200) {
return "somewhat-dissatisfied"
}
if (level < 300) {
return "neither"
}
if (level < 400) {
return "somewhat-satisfied"
}
return "very-satisfied"
}
通过level
的值,返回相应图片的类名,达到笑脸变化的效果。
5. 完整demo.js
代码
import React, { memo } from 'react';
const setSatisfactionClass = (level) => {
if (level < 100) {
return "very-dissatisfied"
}
if (level < 200) {
return "somewhat-dissatisfied"
}
if (level < 300) {
return "neither"
}
if (level < 400) {
return "somewhat-satisfied"
}
return "very-satisfied"
}
const isSameRange = (prevValue, nextValue) => {
const prevValueClass = setSatisfactionClass(
prevValue.level
)
const nextValueClass = setSatisfactionClass(
nextValue.level
)
return prevValueClass === nextValueClass
}
export const FaceComponent = memo((props) => {
const { level } = props;
return (
<div className={setSatisfactionClass(level)}></div>
)
}, isSameRange)
结语
- 该文章来自我录的一个视频,是学习了github上一名叫Lemoncode的一篇react-hooks-by-example的优秀文章,把自己的理解录的小视频放在了b站。
- 我和我一起学习的5个小伙伴把他的文章的每一小节将自己的理解都录成了小视频 响果前端团队React Hooks最佳demo讲解,收获颇多,想学习hooks且有兴趣的小伙伴可以看看噢,欢迎交流与指正。
- 样式文件及图片网址