React Native 性能優化組件-PureComponent

背景

React 使用了 PureComponent,在 shouldComponentUpdate 進行淺比較,這裏到底什麼是淺比較呢?javascript

shallowEqual

在React裏,shouldComponentUpdate源碼爲:java

if (this._compositeType === CompositeTypes.PureClass) {  
  shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState);
}
複製代碼

能夠看到PureComponent就是對props跟state的先後狀態作了一個淺比較。函數

看看shallowEqual的源碼:ui

const hasOwn = Object.prototype.hasOwnPropertyfunctionis(x, y) {
  if(x === y) {
    returnx !==0 || y !==0 ||1/ x ===1/ y  
} else {
    returnx !== x && y !== y  
  }
}
export default function shallowEqual (objA, objB) {
  if ( is(objA, objB )) 
    return true
  i f( typeofobjA !=='object' || objA === null || typeofobjB !=='object' || objB   ===null) {
    return false
}
const keysA = Object.keys ( objA )
const keysB = Object.keys ( objB )
if  (  keysA.length !== keysB.length)
  return false
for ( leti =0; i < keysA.length; i++) {
    if ( !hasOwn.call ( objB, keysA[i] )  || !is(objA[keysA[i]], objB[keysA[i]])) {
      return false
    }  
  }
return true
}
複製代碼

Object.is()

在解析 shallowEqual的源碼以前,先來認識一下 Object.is(),這個函數是用來比較兩個值是否相等。this

爲何要用這個來比較而不是 == 或者 === 呢?spa

==

首先先看 ==,因爲 JS 是弱類型的,若是使用 == 進行比較,== 操做符會自動將 0,‘’(空字符串),null,undefined 轉成布爾型 false,這樣就會出現prototype

0==' '// true
null==undefined// true
[1] ==true// true
複製代碼

這顯然是不符合預期的。因此 JS 爲咱們提供了全等操做符 ===,它不會進行類型轉換,也就是說若是兩個值同樣,必須符合類型也同樣。可是,它仍是有兩種疏漏的狀況code

+0 === -0//true,但咱們期待它返回falseNaN===NaN//false,咱們期待它返回true
複製代碼

因此,Object.is() 修復了 `=== 這兩種判斷不符合預期的狀況,對象

function (x, y){
  // SameValue algorithm
  if(x === y) {
    // 處理爲+0 != -0的狀況
    returnx !==0 || 1/ x ===1/ y;    
  } else {
    // 處理 NaN === NaN的狀況
    return x !== x && y !== y;    
  }
};
複製代碼

這樣就使得 Object.is() 老是返回咱們須要的結果。 它在下面6種狀況下,會返回 trueip

  • 兩個值都是 undefined
  • 兩個值都是 null
  • 兩個值都是 true 或者都是 false
  • 兩個值是由相同個數的字符按照相同的順序組成的字符串
  • 兩個值指向同一個對象
  • 兩個值都是數字而且
    • 都是正零 +0
    • 都是負零 -0
  • 都是 NaN
  • 都是除零和 NaN 外的其它同一個數字

能夠看出Object.is 能夠對基本數據類型: null,undefined,number,string,boolean作出很是精確的比較,可是對於引用數據類型是沒辦法直接比較的。

剖析 shallowEquall

// 用原型鏈的方法
const hasOwn = Object.prototype.hasOwnProperty
// 這個函數其實是 Object.is() 的 polyfill
functionis (x, y) {
  if (x === y) {
    returnx !==0 || y !==0 ||1/ x === 1/ y  
  } else {
    returnx !== x && y !== y  
  }
} 
export default function shallowEqual (objA, objB) {
  // 首先對基本數據類型的比較
  if (is(objA, objB))
    return true
  // 因爲 Obejct.is() 能夠對基本數據類型作一個精確的比較, 因此若是不等
  // 只有一種狀況是誤判的,那就是 object, 因此在判斷兩個對象都不是 object
  // 以後,就能夠返回 false 了
  if (typeofobjA !== 'object' || objA === null || typeofobjB !== '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
}
複製代碼

總結

回到最開始的問題,淺比較爲何沒辦法對嵌套的對象比較?

由上面的分析能夠看到,當對比的類型爲Object的時候而且key的長度相等的時候,淺比較也僅僅是用Object.is()對Object的value作了一個基本數據類型的比較,因此若是key裏面是對象的話,有可能出現比較不符合預期的狀況,因此淺比較是不適用於嵌套類型的比較的。

相關文章
相關標籤/搜索