文章目录
- 一、前言
- 二、区别
- 2.1 PureComponent 缺点
- 2.2 PureComponent 优势
- 2.3 问题剖析
- 三、拓展阅读
一、前言
React.PureComponent
与 React.Component
几乎完全相同,但 React.PureComponent
通过props
和state
的浅对比来实现 shouldComponentUpate()
。
在PureComponent
中,如果包含比较复杂的数据结构,可能会因深层的数据不一致而产生错误的否定判断,导致界面得不到更新。
如果定义了 shouldComponentUpdate()
,无论组件是否是 PureComponent
,它都会执行shouldComponentUpdate()
,并根据结果来判断是否 update
。如果组件未实现 shouldComponentUpdate()
,则会判断该组件是否是 PureComponent
,如果是的话,会对新旧 props
、state
进行 shallowEqual
比较,一旦新旧不一致,会触发 update
。
二、区别
PureComponent
自带通过props
和state
的浅对比来实现 shouldComponentUpate()
,而Component
没有。
2.1 PureComponent 缺点
可能会因深层的数据不一致而产生错误的否定判断,从而shouldComponentUpdate
结果返回false
,界面得不到更新。
2.2 PureComponent 优势
不需要开发者自己实现shouldComponentUpdate
,就可以进行简单的判断来提升性能。
2.3 问题剖析
为什么PureComponent
比较复杂的数据结构,可能会因深层的数据不一致而产生错误的否定判断?
JavaScript
中的对象一般是可变的(Mutable
),因为使用了引用赋值,新的对象简单的引用了原始对象,改变新的对象将影响到原始对象。如 foo={a: 1}; bar=foo; bar.a=2
你会发现此时 foo.a 也被改成了 2。
为了解决这个问题,一般的做法是使用 shallowCopy
(浅拷贝)或 deepCopy
(深拷贝)来避免被修改,但这样做造成了 CPU 和内存的浪费。
接下来分析shallowEqual()
函数
function shallowEqual(objA: mixed, objB: mixed): boolean {// 首先对两个基本数据类型进行比较if (is(objA, objB)) {return true;}// 判断两个数据都为object的情况if (typeof objA !== 'object' || objA === null ||typeof objB !== 'object' || objB === null) {return false;}// 获得所有的keyconst keysA = Object.keys(objA);const keysB = Object.keys(objB);// 判断两者key的数量是否一致if (keysA.length !== keysB.length) {return false;}// 如果key数量相同,使用一层for循环去比较for (let i = 0; i < keysA.length; i++) {if (// 判断对象B中是否包含对象A的key,即两者的keys是否一致!hasOwnProperty.call(objB, keysA[i]) ||// 通过is()函数对比A和B的key对应的数据!is(objA[keysA[i]], objB[keysA[i]])) {return false;}}
下面以组件的使用来举例:
例如:
class ChildComponent extends React.PureComponent {render() {return(<div>{this.props.numbers}</div>)}
}
class MainComponent extends React.Component {constructor(props) {super(props);this.handleClick = this.handleClick.bind(this);this.state = {numbers: [0]}}handleClick() {const arr = this.state.numbers;arr.push(1);this.setState({numbers: arr})console.log(this.state.numbers)}render() {<div><button onClick={this.handleClick} /><ChildComponent numbers={this.state.numbers}/></div>}
}
在MainComponent中去修改numbers时,ChildComponent并没有得到刷新。原因在于js使用的是引用赋值,新的对象简单引用了原始对象,改变新对象虽然影响了原始对象,但对象的地址还是一样,使用===
比较的方式相等。而在PureComponent
中,会被判定prop
相等而不触发render()
。
避免此类问题最简单的方式是,避免使用值可能会突变的属性或状态,而是使用副本来返回新的变量。
handleClick() {this.setState(prevState => ({numbers: [...prevState.numbers, 1],}));
};
三、拓展阅读
- 《ReactNative专栏》
本文链接:https://my.lmcjl.com/post/12351.html
4 评论