首先歡迎你們關注個人Github博客,也算是對個人一點鼓勵,畢竟寫東西無法得到變現,能堅持下去也是靠的是本身的熱情和你們的鼓勵,但願你們多多關注呀!react的部分持續更新中...react
React15.3中新加了一個PureComponent類,取代以前的PureRenderMixin , PureComponent能夠進行React性能優化,減小沒必要要的render渲染次數,使用時只要把繼承類從Component換成PureComponent。git
PureComponent的原理是繼承了Component類,自動加載shouldComponentUpdate函數,當組件更新時,shouldComponentUpdate對props和state進行了一層淺比較,若是組件的props和state都沒有發生改變,render方法就不會觸發,省去Virtual DOM的生成和對比過程,達到提高性能的目的。github
下面從PureComponent的使用和源碼來完全瞭解它.npm
在PureComponent以前,咱們常常看到優化react性能最多見的手段之一就是在react的生命週期函數shouldComponentUpdate裏判斷props或state的數據是否發生變化,經過返回ture(更新)和false(不更新)來阻止沒必要要的render.瀏覽器
首先來看看react產生沒必要要渲染的一個場景:性能優化
用create-react-app來初始化react的運行環境,而後在App.js文件裏建立一個定時任務,每隔一秒更新數據:app
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組件內容爲:frontend
export default class List extends Component {
render() {
console.log('list render')
return(
<div>{this.props.list.title}</div>
)
}
}
複製代碼
命令行運行npm start,瀏覽器查看輸出:函數
發現控制檯每隔一秒都會輸出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.memo(() => (
<div>{this.props.data || 'loading'}</div>
))
複製代碼
注:上面涉及到的全部:point_right:源代碼