在React写类组件的时候,类组件都会extends
继承React.PureComponent
或者React.Component
,那为什么要继承React.PureComponent
或者React.Component
呢?,我们都知道PureComponent
和Component
都是React中的组件类,它们都可以用来写类组件,那它们的作用是什么呢?它们又有什么区别呢?
Component
Component
是React中定义组件的基类,它的shouldComponentUpdate
方法默认返回true
,也就是说,每次调用setState
方法都会引发组件重新渲染。如果希望在一些情况下不重新渲染组件,就需要在继承自Component
的组件中手动实现shouldComponentUpdate
方法来进行比较。
下面来看一段代码:
class Child extends React.Component{
state = {
count:0
}
// 点击改变状态了
changeCount = () => {
let { count } = this.state
this.setState({
count:++count
})
}
// 点击按钮状态只是赋了相同的值,并没有改变
constCount = () => {
this.setState({
count:this.state.count
})
}
render(){
console.log('执行了---render')
let { count } = this.state
return <div className="child">
<div>当前状态: { count }</div>
<div><button onClick={this.changeCount.bind(null)}>改变状态</button></div>
<div><button onClick={this.constCount.bind(null)}>不改变状态</button></div>
</div>
}
}
上面代码每次点击"改变状态"按钮,都会触发changeCount
方法通过setState
改变状态,每次点击都触发了render
函数;然而当点击"不改变状态"按钮,都会触发constCount
方法通过setState
重新给count
赋值且并没有改变值的状态,在这种情况下每次点击按钮也触发了render
函数,像这种情况,可以考虑在没有状态改变的情况下,不用触发render
函数,从而减少页面渲染优化性能。
通过更新前后的state值是否相同来判断是否需要执行render函数:
class Child extends React.Component{
state = {
count:0
}
// 点击改变状态了
changeCount = () => {
let { count } = this.state
this.setState({
count:++count
})
}
// 点击按钮状态只是赋了相同的值,并没有改变
constCount = () => {
this.setState({
count:this.state.count
})
}
// 通过更新前后的state值是否相同来判断是否需要执行render函数
shouldComponentUpdate(props,state){
console.log(props,state)
let { count } = this.state
if(state.count === count){
return false
}
return true
}
render(){
console.log('执行了---render')
let { count } = this.state
return <div className="child">
<div>当前状态: { count }</div>
<div><button onClick={this.changeCount.bind(null)}>改变状态</button></div>
<div><button onClick={this.constCount.bind(null)}>不改变状态</button></div>
</div>
}
}
上面代码增加了个shouldComponentUpdate
函数,函数包含两个参数,第一个是即将更新的 props
值,第二个是即将更新后的state
值,这里通过判断state
的值是否和组件状态里面的count
的值是否相等来返回false
或true
,如果相等就返回false
就不更新组件,也就不执行render
函数;如果不相等就返回true
就需要更新组件,执行render
函数重新渲染页面,这样就减少了页面不必要的渲染。
小结:这里组件继承了React.Component
需要手动实现shouldComponentUpdate
函数来对比状态是否改变,从而判断是否需要执行render
函数重新渲染页面。
PureComponent
PureComponent
则提供了一种基于浅比较的优化机制,它默认实现了shouldComponentUpdate
方法,会自动对组件的props
和state
进行浅比较,如果发现props
或state
没有发生变化,则阻止组件的重新渲染。
看下面这段代码:
class Child extends React.PureComponent{
state = {
count:0
}
// 点击改变状态了
changeCount = () => {
let { count } = this.state
this.setState({
count:++count
})
}
// 点击按钮状态只是赋了相同的值,并没有改变
constCount = () => {
this.setState({
count:this.state.count
})
}
render(){
console.log('执行了---render')
let { count } = this.state
return <div className="child">
<div>当前状态: { count }</div>
<div><button onClick={this.changeCount.bind(null)}>改变状态</button></div>
<div><button onClick={this.constCount.bind(null)}>不改变状态</button></div>
</div>
}
}
上面代码当点击"改变状态"按钮,都会触发changeCount
方法通过setState
改变状态,每次点击都触发了render
函数;然而当点击"不改变状态"按钮,都会触发constCount
方法通过setState
重新给count
赋值且并没有改变值的状态,在这种情况下每次点击按钮后render
函数并没有执行,这是因为在PureComponent
方法里默认实现了shouldComponentUpdate
方法,会自动对组件的props
和state
进行浅比较,如果发现props
或state
没有发生变化,则不会执行render
函数,也就不会使组件重新渲染。
如果深层的状态也只是赋个相同的值,会执行render
函数?
class Child extends React.PureComponent{
state = {
count:0,
obj:{
num:0
}
}
// 点击啊按钮只是赋了相同的值深层的状态
changeNum = () => {
let { obj } = this.state
this.setState({
obj:{
num: obj.num
}
})
}
render(){
console.log('执行了---render')
let { obj } = this.state
return <div className="child">
<div>当前状态num: { obj.num }</div>
<div><button onClick={this.changeNum.bind(null)}>深层的状态</button></div>
</div>
}
}
上面代码当点击深层的状态
按钮时,也只是给num
赋了相同的值不做改变,然而却执行render
函数,也就是说PureComponent
做了浅比较。
PureComponent
是怎么去做浅比较的呢?
// 检测是否为对象
const isObject = function(obj){
return obj !== null && /^(object|function)$/.test(typeof obj)
}
// 浅比较函数
function shallowEqual(objA, objB){
if(!isObject(objA) || !isObject(objB)) return false
if(objA === objB) return true
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
// 比较key的数量
if (keysA.length !== keysB.length) return false
// 两个对象key的数量一致,逐一比较内部成员
for (let i = 0; i < keysA.length; i++) {
let key = keysA[i]
//判断objB中是否存在objA的key; 如果存在相同的key,判断key对应的值是否相等
if(!Object.hasOwnProperty.call(objB,key) || !Object.is(objA[key], objB[key])){
return false
}
}
return true
}
let k = [1,2]
let a = {
a:1,
b:k
}
let b = {
a:1,
b:k
}
console.log(shallowEqual(a,b)) // true
以上就是PureComponent
相当于做了这样的一个浅对比的事情,如果是深层的就不会再去比较了,比如:
//......
let a = {
a:1,
b:[1,2]
}
let b = {
a:1,
b:[1,2]
}
console.log(shallowEqual(a,b)) // false
如果改成以上代码就不相等了,虽然b
的值都是相同的数组,但在栈中却是不同的引用指针。
总结
PureComponent
其实就是一个继承自Component
的子类,会自动加载shouldComponentUpdate
函数。当组件需要更新的时候,shouldComponentUpdate
会对组件的props
和state
进行一次浅比较。如果props
和state
都没有发生变化,那么render
方法也就不会触发,在react性能方面得到了优化。