结论 PureComponent是在Component的基础上对shouldComponentUpdate这部分进行了优化,通过对新旧state和新旧props各进行一次浅比较,通过判断是否发生变化来确认是否需要进行更新,以此来避免大量的重复渲染,优化性能。我们通过一段代码来看下两者的行为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 class ChildComponent extends React .Component { render() { console .log('ChildComponent render' ) return ( <div> {this .props.numbers} </div> ) } } class ChildComponent extends React.PureComponent { render() { console.log('ChildComponent 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 }) } render() { console .log('MainComponent render' ) return ( <div> <button onClick={this .handleClick} >click me</button> <ChildComponent numbers={this.state.numbers}/ > </div> ) } } export default MainComponent
在点击按钮时,MainComponent每次都会执行render函数,但ChildComponent只有继承PureComponent时,才会执行render,在react的源码中,PureComponent在判断是否需要更新组件时,进行了以下的比较:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 shouldUpdate = !shallowEqual(oldProps, props) || !shallowEqual(oldState, state); function is (x: any, y: any ) { return ( (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) ); } const hasOwnProperty = Object .prototype.hasOwnProperty;function shallowEqual (objA: mixed, objB: mixed ): boolean { if (is(objA, objB)) { return true ; } if ( typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null ) { return false ; } const keysA = Object .keys(objA); const keysB = Object .keys(objB); if (keysA.length !== keysB.length) { return false ; } for (let i = 0 ; i < keysA.length; i++) { if ( !hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]]) ) { return false ; } } return true ; }
注意:这个代码示例中, handleClick 方法中给state对象push元素时,用的是引用的方式,所以,当使用PureComponent时,因为我们操作的时同一个数据对象,所以就算修改了state,组件也不会进行更新,但是因为Component不进行这个处理,所以页面的数据可以正常发生变化,这也是使用PureComponent必须留意的地方。我们必须通过浅拷贝或者immutable的一些库,来避免这种错误的发生。 用浅拷贝的修改方式如下
1 2 3 4 5 6 7 8 handleClick() { const arr = this .state.numbers; let newArr = [...arr] newArr.push(1 ) this .setState({ numbers: newArr }) }
用immutableJS的修改方式如下
1 2 3 4 5 6 7 8 9 10 11 12 13 this .state = { numbers: List([0 ]) } handleClick() { const arr = this .state.numbers; let newArr = arr.push(1 ) this .setState({ numbers: newArr }) }