React生命周期shouldComponentUpdate介绍及性能优化
在react开发中,经常需要对数据state状态进行改变,但是这种方式每当setState的时候都会将所有的组件重新渲染一遍,这样就会有重复渲染render的问题。
看如下图组件树:
默认情况下,当执行setState()方法时,react 会重新渲染整个组件树,这造成不必要的性能开销。
黄色的节点表示我们修改了数据的节点,我们希望只重新渲染这个部分,而其它蓝色的节点是我们不希望重复渲染的。那么怎么解决呢?生命周期的shouldComponentUpdate就可以简单解决。
shouldComponentUpdate介绍
shouldComponentUpdate(nextProps, nextState)
- nextProps: 表示下一个props。
- nextState: 表示下一个state的值。
官方API解释道根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。默认行为是 state 每次发生变化组件都会重新渲染。大部分情况下,你应该遵循默认行为。
shouldComponentUpdate(nextProps, nextState){
//组件是否需要更新,需要返回一个布尔值,返回true则更新,返回flase不更新,这是一个关键点
console.log('shouldComponentUpdate组件是否应该更新,需要返回布尔值',nextProps, nextState)
return true
}
当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。返回值默认为 true。首次渲染或使用 forceUpdate() 时不会调用该方法。
使用shouldComponentUpdate性能优化
React中props,state值的变化,会导致组件重新渲染。使用shouldComponentUpdate就是为了减少render不必要的渲染。 返回布尔值,然后做 Virtual DOM 比较,并得出是否需要做真实 DOM 更新,尽管React的虚拟算法复杂度已经有了很多优化,但是在大规模组件更新时,依然会是个不必要的损耗。会带来很多无必要的渲染并成为性能瓶颈。接下来使用来这个生命周期来解决吧
例子:
import React, { Component } from 'react'
class Title extends Component {
shouldComponentUpdate(nextProps){
return nextProps.title!==this.props.title
}
render() {
console.log("我是title组件")
return (
<div>
标题:{this.props.title}
</div>
)
}
}
class Count extends Component {
render() {
console.log("我是条数组件")
return (
<div>
条数:{this.props.count}
</div>
)
}
}
export default class Purememo extends Component {
constructor(props){
super(props)
this.state={
title:'shouldComponentUpdate使用',
count:0
}
}
componentDidMount(){
setInterval(()=>{
this.setState({
count:this.state.count+1
})
},1000)
}
render() {
return (
<div>
<Title title={this.state.title}></Title>
<Count count={this.state.count}></Count>
</div>
)
}
}
Purememo整个组件会重新渲染,如果不适用shouldComponentUpdate那么Title,Count会一直渲染,但是使用shouldComponentUpdate后Title则不会发生渲染。
当然你也可以考虑使用内置的 React.PureComponent 组件,shouldComponentUpdate()。PureComponent 会对 props 和 state 进行浅层比较,并减少了跳过必要更新的可能性。不建议shouldComponentUpdate() 中进行深层比较或使用 JSON.stringify()。这样非常影响效率,且会损害性能。
面对复杂的对象时,这样的数据就没有效果了,因为在js中,object,array,function属于引用类型,即使改变其中数据,他们指向的还是同一内存地址,所以采用上面的判断就不行了。例如:
obj: {
age: 12,
name: 'xiaoming',
student: {
count: 1
}
}
解决方法有以下几种:
- 使用setState改变数据之前,先采用es6中assgin进行拷贝,但是assgin只深拷贝的数据的第一层,所以说不是最完美的解决办法。
const o2 = Object.assign({},this.state.obj)
o2.student.count = '00000';
this.setState({
obj: o2,
})
- 使用JSON.parse(JSON.stringfy())进行深拷贝,但是遇到数据为undefined和函数时就会错。
const o2 = JSON.parse(JSON.stringify(this.state.obj))
o2.student.count = '00000';
this.setState({
obj: o2,
})
- 使用react官方推荐的第三方库:immutable.js进行项目的搭建。immutable中讲究数据的不可变性,每次对数据进行操作前,都会自动的对数据进行深拷贝,项目中数据采用immutable的方式,可以轻松解决问题,但是又多了一套API去学习。
关于immutable.js可参考
精读 Immutable 结构共享
深入探究Immutable.js的实现机制(一)
深入探究Immutable.js的实现机制(二)
了解Clojure的持久变量
部分参考他人,侵删