React15.3中新加了一个PureComponent类,取代以前的PureRenderMixin , PureComponent能够进行React性能优化,减小没必要要的render渲染次数,使用时只要把继承类从Component换成PureComponent。react
PureComponent的原理是继承了Component类,自动加载shouldComponentUpdate函数,当组件更新时,shouldComponentUpdate对props和state进行了一层浅比较,若是组件的props和state都没有发生改变,render方法就不会触发,省去Virtual DOM的生成和对比过程,达到提高性能的目的。git
下面从PureComponent的使用和源码来完全了解它.github
在PureComponent以前,咱们常常看到优化react性能最多见的手段之一就是在react的生命周期函数shouldComponentUpdate里判断props或state的数据是否发生变化,经过返回ture(更新)和false(不更新)来阻止没必要要的render.npm
首先来看看react产生没必要要渲染的一个场景:浏览器
用create-react-app来初始化react的运行环境,而后在App.js文件里建立一个定时任务,每隔一秒更新数据:性能优化
import React, { Component } from 'react' import ShouldComponentUpdateList from './ShouldComponentUpdateList' // 容器组件 export default class App extends Component { state = { data: [] } componentDidMount() { // 定时任务,每隔一秒更新数据 setInterval(() => { this.setState({ data: [ { title: 'react line 1' }, { title: 'react line 2' }, ] }) }, 1000) } render() { return( <div> { this.state.data.map((item, index) => ( <ShouldComponentUpdateList key={index} list={item} /> )) } </div> ) } }
ShouldComponentUpdateList组件内容为:app
export default class List extends Component { render() { console.log('list render') return( <div>{this.props.list.title}</div> ) } }
命令行运行npm start,浏览器查看输出:frontend
发现控制台每隔一秒都会输出list render
,明明数据没有发生变化,可是react仍是发生渲染,形成了没必要要的渲染浪费。函数
只须要在shuoldComponentUpdate里加上判断,再次查看输出结果,定时任务的数据没有发生改变,不会再渲染render函数:性能
export default class List extends Component { // 在shuoldComponentUpdate里判断props传递的数据没有发生变化,则不须要render shouldComponentUpdate(nextProps) { // 返回值为true则render,为false则不render if(nextProps.list.title === this.props.list.title) { return false } return true } render() { console.log('list render') return( <div>{this.props.list.title}</div> ) } }
除了使用shouldComponentUpdate来判断是否须要更新组件,还能够用PureComponent, PureComponent实际上自动加载shouldComponentUpdate函数,当组件更新时,shouldComponentUpdate对props和state进行了一层浅比较.
新建PureComponentList组件,用PureComponent代替Component:
import React, { PureComponent } from 'react' export default class List extends PureComponent { render() { console.log('list render') return( <div>{this.props.list.title}</div> ) } }
在App组件中传入:
this.state.data.map((item, index) => ( <PureComponentList key={index} list={item}/> ))
而后查看浏览器输出结果,惊奇地发生,PureComponent并无阻止没必要要render,这是为何呢?由于前面咱们说到PureComponent的shouldComponentUpdate只对props和state进行浅比较,也就是this.props = { list: { title: 'react line1' } }
,nextProps = { list: { title: 'react line1' } }
,做浅比较的话this.props固然不等于next.props.
为了更清晰地找到缘由,咱们先来看看PureComponent的源码.
首先找到PureComponent这个函数,在构造函数和原型上分别继承了Component的属性和方法:
export default function PureComponent(props, context) { Component.call(this, props, context) } PureComponent.prototype = Object.create(Component.prototype) PureComponent.prototype.constructor = PureComponent PureComponent.prototype.shouldComponentUpdate = shallowCompare function shallowCompare(nexProps, nextState) { return !shallowEqual(this.props, nextProps) || !shollowEqual(this.state, nextState) }
接着PureComponent在生命周期函数里面写了shallowCompare方法,shallowCompare里面经过shallowEqual的返回值来返回ture仍是false.
接着来看看shallowEqual函数,源码地址:
export default function shallEqual(objA, objB) { // 从后面代码能够看出,对于两个对象的比较为这里的代码 if (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; } // Test for A's keys different from B. for (let i = 0; i < keysA.length; i++) { if (!objB.hasOwnProperty(keysA[i]) || objA[keysA[i]] !== objB[keysA[i]]) { return false; } } return true; }
从上面浅比较的源码shallowEqual函数能够看出,shallEqual对于对象的比较仅仅经过if (objA === objB) { return true; }
来判断,而let a = { list: { title: 'react line1' } }
,let b = { list: { title: 'react line1' } }
, a === b值为false,因此这就很好的解释了上面PureComponent并无阻止没必要要render的缘由。
因此咱们来改进代码,使得PureComponent的props若是传入对象状况下应该如何起效:
在App.js里面修改PureComponentList组件传入item.title而不是item对象,浏览器只输出两次log:
this.state.data.map((item, index) => ( // <PureComponentList key={index} list={item}/> <PureComponentList key={index} title={item.title}/> ))
经过解构item对象,传入item.title,这样就能够进行浅比较,来达到优化没必要要渲染的目的.
由上面探究PureComponent源码咱们知道,PureComponent的组件在props或者state的属性值是对象的状况下,并不能阻止没必要要的渲染,是由于自动加载的shouldComponentUpdate里面作的只是浅比较,因此想要用PureComponent的特性,应该遵照原则:
在使用PureComponent的时候,只能把react组件写成是class的形式,不能使用函数的形式;react v16.6.0以后,可使用React.memo来实现函数式的组件,也有了PureComponent的功能。
List组件的PureComponent:
const ListComponent = React.momo(() => ( <div>{this.props.data || 'loading'}</div> ))
注:上面涉及到的全部源代码