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> ))
注:上面涉及到的全部源代碼