前言
介紹之前可以先看下shouldComponentUpdate介紹
PureComponent介紹
React15.3中新加了一個 PureComponent 類,顧名思義, pure 是純的意思, PureComponent 也就是純組件,取代其前身 PureRenderMixin , PureComponent 是優化 React 應用程序最重要的方法之一,易於實施,只要把繼承類從 Component 換成 PureComponent 即可,可以減少不必要的 render 操作的次數,從而提高性能,而且可以少寫 shouldComponentUpdate 函數(shouldComponentUpdate通過判斷props和state是否發生變化來決定需不需要重新渲染組件),主要目的就是防止不必要的子組件渲染更新。
PureComponent原理
當組件更新時,如果組件的 props 和 state 都沒發生改變, render 方法就不會觸發,省去 Virtual DOM 的生成和比對過程,達到提升性能的目的。具體就是 React 自動幫我們做了一層淺比較:
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps)
|| !shallowEqual(inst.state, nextState);
}
而 shallowEqual 又做了什麼呢?會比較 Object.keys(state | props) 的長度是否一致,每一個 key 是否兩者都有,並且是否是一個引用。
簡單用代碼實現:
/*
* React.PureComponent
*/
// 把兩個對象進行淺比較
// 只比較對象的第一級
// 如果屬性值是基本類型的,我們只需要比較值是否一樣即可
function shallowEqual(obj1, obj2) {
if (Object.keys(obj1).length !== Object.keys(obj2).length){
return false;
}
for (let key in obj1) {
if (obj1[key] !== obj2[key]) {
return false;
}
}
return true;
}
源碼:
// 淺比較shallowEqual的源碼
const hasOwn = Object.prototype.hasOwnProperty
// 這個函數實際上是Object.is()的polyfill
function is(x, y) {
if (x === y) {
return x !== 0 || y !== 0 || 1 / x === 1 / y
} else {
return x !== x && y !== y
}
}
export default function shallowEqual(objA, objB) {
// 首先對基本數據類型的比較
if (is(objA, objB)) return true
// 由於Obejct.is()可以對基本數據類型做一個精確的比較, 所以如果不等
// 只有一種情況是誤判的,那就是object,所以在判斷兩個對象都不是object之後,就可以返回false了
if (typeof objA !== 'object' || objA === null ||
typeof objB !== 'object' || objB === null) {
return false
}
// 過濾掉基本數據類型之後,就是對對象的比較了
// 首先拿出key值,對key的長度進行對比
const keysA = Object.keys(objA)
const keysB = Object.keys(objB)
// 長度不等直接返回false
if (keysA.length !== keysB.length) return false
for (let i = 0; i < keysA.length; i++) {
// key值相等的時候
// 借用原型鏈上真正的 hasOwnProperty 方法,判斷ObjB裏面是否有A的key的key值
// 屬性的順序不影響結果也就是{name:'daisy', age:'24'} 跟{age:'24',name:'daisy' }是一樣的
// 最後,對對象的value進行一個基本數據類型的比較,返回結果
if (!hasOwn.call(objB, keysA[i]) ||
!is(objA[keysA[i]], objB[keysA[i]])) {
return false
}
}
return true
}
淺比較就是隻比較第一級,對於基本數據類型,只比較值;對於引用數據類型值,直接比較地址是否相同,不管裏面內容變不變,只要地址一樣,我們就認爲沒變。所以在這種情況下,我們以後用的時候,對於引用類型值修改狀態或修改屬性時候,對於它賦值的時候,我們儘可能把之前值拿過來克隆一份,賦給它新的地址就好~這是我們的注意點!我們想做性能優化的時候就可以在Component裏做一個淺比較。
總結下就是React.PureComponent是基於淺比較,所以只要屬性值是引用類型,但是修改後的值變了,但是地址不變,也不會重新渲染。在深層數據結構發生變化時可以調用 forceUpdate() 來確保組件被正確地更新。也可以用 immutable 對象加速嵌套數據的比較。
PureComponent使用(在class組件使用)
import React, { Component ,PureComponent} from 'react'
class Title extends PureComponent {
render() {
console.log("我是title組件")
return (
<div>
標題:{this.props.title}
</div>
)
}
}
class Count extends Component {
render() {
console.log("我是條數組件")
return (
<div>
條數:{this.props.count}
</div>
)
}
}
export default class Purememo extends Component {
constructor(props){
super(props)
this.state={
title:'shouldComponentUpdate使用',
count:0
}
}
componentDidMount(){
setInterval(()=>{
this.setState({
count:this.state.count+1
})
},1000)
}
render() {
return (
<div>
<Title title={this.state.title}></Title>
<Count count={this.state.count}></Count>
</div>
)
}
}
PureComponent與shouldComponentUpdate 共存
經過上面的簡單介紹,其實可以知道如果 PureComponent 裏有 shouldComponentUpdate 函數的話,直接使用 shouldComponentUpdate 的結果作爲是否更新的依據,沒有 shouldComponentUpdate 函數的話,纔會去判斷是不是 PureComponent ,是的話再去做 shallowEqual 淺比較。
// 這個變量用來控制組件是否需要更新
var shouldUpdate = true;
// inst 是組件實例
if (inst.shouldComponentUpdate) {
shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext);
} else {
if (this._compositeType === CompositeType.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps) ||
!shallowEqual(inst.state, nextState);
}
}
與老版本共存
import React { PureComponent, Component } from 'react';
class App extends (PureComponent || Component) {
//...
}
注:PureComponent不可濫用,他使用在class組件內,只有那些狀態和屬性不經常的更新的組件我們用來做優化,對於經常更新的,這樣處理後反而浪費性能,因爲每一次淺比較也是要消耗時間的。