父组件进行了setState,众所周知子组件都会进行重新渲染。React.memo
会做浅层对比传入的props
,如果传入的props
没有改变,这个子组件就不会重新渲染。
React.memo
为高阶组件。它与React.PureComponent
非常相似,但它适用于函数组件,但不适用于 class 组件。
系列目录
- React新特性-Context(一)
- React新特性-Lazy和Suspense(二)
- React新特性-memo(三)
- React新特性-Hooks之useState(四)
- React新特性-Hooks之useEffect和useLayoutEffect(五)
- React新特性-Hooks之useContext(六)
- React新特性-Hooks之useRef(七)
- React新特性-Hooks之useMemo和useCallback(八)
- React新特性-Hooks之useReducer(九)
- React新特性-Hooks之自定义Hook(十完结)
React性能优化之shouldComponentUpdate
shouldComponentUpdate(nextProps, nextState)
是React一个生命周期函数,这个函数需要返回一个Boolean
值来判断是否要重新渲染。
当 props
或 state
发生变化时,shouldComponentUpdate()
会在渲染执行之前被调用,返回值默认为 true
。
此方法仅作为性能优化的方式而存在
下面示例:
//App.js
import React, { Component } from 'react';
import TestButton from './TestButton';
export default class App extends Component {
state = {
inputValue: '',
num:1,
}
render() {
return (
<div className="App">
<input
value={this.state.inputValue}
onChange={(e)=>this.setState({inputValue:e.target.value})}
/>
<TestButton num={this.state.num}/>
</div>
)
}
}
//TestButton.js
import React, { Component } from 'react'
export default class TestButton extends Component{
render(){
console.log('我是TestButton组件,我被渲染啦');
return (
<div>
数字:{this.props.num}
<button>提交+1</button>
</div>
)
}
}
从上面代码中可以看出TestButton
是App
组件的子组件。App组件所作用的事情就是使input输入框受控。
从上面的图片可以看出,当页面加载的时候App
渲染一次,然后TestButton
渲染一次。
当input输入变更state
的时候,App
在不停的重新渲染得到界面更新。但是TestButton
也在不停的渲染,TestButton
完全没有必要渲染,因为他的state
和props
都未曾改变。这时候就可以进行性能优化,去掉不必要的渲染。
如下:
//App.js
import React, { Component } from 'react';
import TestButton from './TestButton';
export default class App extends Component {
state = {
inputValue: '',
num:1,
}
render() {
return (
<div className="App">
<input
value={this.state.inputValue}
onChange={(e)=>this.setState({inputValue:e.target.value})}
/>
<TestButton num={this.state.num} onClick={()=>{
this.setState((prevState)=>({num:prevState.num+1}))
}}/>
</div>
)
}
}
//TestButton.js
import React, { Component } from 'react'
export default class TestButton extends Component{
//手动对比是否要重新渲染
shouldComponentUpdate(props){
if(props.num !== this.props.num){
return true
}
return false
}
render(){
console.log('我TestButton组件,我被渲染啦');
return (
<div>
数字:{this.props.num}
<button onClick={this.props.onClick}>提交+1</button>
</div>
)
}
}
这次我们在TestButton
组件中加入了shouldComponentUpdate
生命周期函数来手动判断是否要重新渲染组件来达到性能优化
从图二中可以看出input怎么输入TestButton
都不为所动,只有当num
的值进行了state
变更才会重新渲染TestButton
组件
React性能优化之React.PureComponent
每次在组件中使用shouldComponentUpdate
就有点麻烦,有没有自动对比数据自动判断是否要更新呢? 当然有,React提供了React.PureComponent
来做这件事情
React.PureComponent
中的shouldComponentUpdate()
仅作对象的浅层比较。如果对象中包含复杂的数据结构,则有可能因为无法检查深层的差别
我们来看是示例:
//仅仅修改TestButton.js
import React, { PureComponent } from 'react'
//继承PureComponent组件来做自动props对比
export default class TestButton extends PureComponent{
render(){
console.log('我TestButton组件,我被渲染啦');
return (
<div>
数字:{this.props.num}
<button onClick={this.props.onClick}>提交+1</button>
</div>
)
}
}
可以看出 TestButton
继承了PureComponent
组件,然后我们在输入试试。这时候发现input输入还是会引起TestButton渲染,这是为什么呢?答案是:
//App.js
//当组件重新渲染onClick事件每次就会生成新的函数
<TestButton num={this.state.num} onClick={()=>{
this.setState((prevState)=>({num:prevState.num+1}))
}}/>
由于每次输入造成重新渲染,而onClick
事件中的回调函数每次也会重新生成函数,导致传入的props
中onClick
的值(引用地址)在不停的变更新。
这样PureComponent
在做对比的时候就认为props
每次接受到的都是一个新的值而不得不去更新组件。
想要解决我们把代码改成如下这样:
import React, { Component } from 'react';
import TestButton from './TestButton';
export default class App extends Component {
state = {
inputValue: '',
num:1,
}
//App.prototype.onClick=()=>xxxxxx 每次重新渲染onClick得到的函数地址始终是不变的
onClick=()=>{
this.setState((prevState)=>({num:prevState.num+1}))
}
render() {
return (
<div className="App">
<input
value={this.state.inputValue}
onChange={(e)=>this.setState({inputValue:e.target.value})}
/>
<TestButton num={this.state.num} onClick={this.onClick}/>
</div>
)
}
}
每次重新渲染onClick得到的函数地址始终是不变的,这样就解决了TestButton
重新渲染的问题
React性能优化之React.memo
讲了这么多,下面请出React.memo()
函数,其实上面已经都把memo的工作原理全部展现出来了。
按照React官方的说法TestButton
组件是一个无状态组件,可以用函数组件来代替class组件来提高优化。
示例:
import React, { memo } from 'react'
const TestButton = (props) => {
console.log('我TestButton组件,我被渲染啦');
return (
<div>
数字:{props.num}
<button onClick={props.onClick}>提交+1</button>
</div>
)
}
//memo是高阶组件
export default memo(TestButton)
因为是函数组件既无生命周期函数shouldComponentUpdate
可用,也不能继承PureComponent
,所以React
提供了memo
使用来达到一样的效果,其使用原理都和上面一样。
上面例子,用memo
这个高阶组件包裹TestButton
函数组件,当父组件输入setState
时候子组件memo
自动判断num
是否有变化,如果没有就不会重新渲染TestButton
组件
拆分细的组件,使用到的优化机会就越多。
function areEqual(prevProps, nextProps) {
/*
如果把 nextProps 传入 render 方法的返回结果与
将 prevProps 传入 render 方法的返回结果一致则返回 true,
否则返回 false
*/
}
export default React.memo(MyComponent, areEqual);
memo也支持手动对比,类似shouldComponentUpdate
函数一样来判断