為什么用 PureComponent
?
PureComponent
是優化React
應用程序最重要的方法之一,易于實施,只要把繼承類從Component
換成PureComponent
即可,可以減少不必要的render
操作的次數,從而提高性能,而且可以少寫shouldComponentUpdate
函數,節省了點代碼。
原理
當組件更新時,如果組件的
props
和state
都沒發生改變,render
方法就不會觸發,省去Virtual DOM
的生成和比對過程,達到提升性能的目的。
具體就是由于
PureComponent
的shouldeComponentUpdate
里,實際是對props/state
進行了一個
淺對比,所以對于嵌套的對象不適用,沒辦法比較出來。
淺對比??
首先讓我們來看下 PureComponent
的具體實現
在React
里,shouldComponentUpdate
源碼為:
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps)
|| !shallowEqual(inst.state, nextState);
}
那我們再來看看 shallowEqual
的源碼到底是神馬玩意?
const hasOwn = Object.prototype.hasOwnProperty
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
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 (!hasOwn.call(objB, keysA[i]) ||
!is(objA[keysA[i]], objB[keysA[i]])) {
return false
}
}
return true
}
一看上去可能直接蒙圈。接下來我們一步一步來看。
Object.is()
1. Object.is() 這個函數是用來比較兩個值是否相等。但這并不同于 === 或者 ==
2. '==' 比較,會把 undefined, null, '', 0 直接轉換成布爾型false
null == undefined // true
3. '===' 它不會進行類型轉換,也就是說如果兩個值一樣,必須符合類型也一樣。但是,它還是有兩種疏漏的情況。
+0 === -0 // true,但我們期待它返回false
NaN === NaN // false,我們期待它返回true
正因為這些原因,Object.is()
應運而生
// 實際上是Object.is()的polyfill
function(x, y) {
// SameValue algorithm
if (x === y) {
// 處理為+0 != -0的情況
return x !== 0 || 1 / x === 1 / y;
} else {
// 處理 NaN === NaN的情況
return x !== x && y !== y;
}
};
了解這個我們再來看看剛才的 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) {
// 首先對基本數據類型的比較
// !! 若是同引用便會返回 true
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
// key相等的情況下,在去循環比較
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
}
總結: shallowEqual
會比較 Object.keys(state | props)
的長度是否一致,每一個 key
是否兩者都有,并且是否是 一個引用,也就是只比較了 第一層 的值,確實很淺,所以深層的嵌套數據是對比不出來的。
案例 (易變數據不能使用一個引用)
import React, { PureComponent } from 'react'
class App extends PureComponent {
state = {
items: [1, 2, 3]
}
handleClick = () => {
const { items } = this.state
items.pop()
this.setState({ items })
}
render () {
return (<div>
<ul>
{this.state.items.map(i => <li key={i}>{i}</li>)}
</ul>
<button onClick={this.handleClick}>delete</button>
</div>)
}
}
通過這個案例就會發現無論怎么點 delete
按鈕 li
的數量都不會改變。就是因為 items
用的是一個引用, shallowEqual
的結果為 true
。改正:
handleClick = () => {
const { items } = this.state;
items.pop();
this.setState({ items: [].concat(items) });
}
菜鳥有話說。。
以上及是剛學習React
的新手對于PureComponent
的淺顯認識。閱讀并借鑒了幾位大神的文章,第一次分享與廣大初學者共同交流進步。努力!奮斗!????