React PureComponent 學習及淺比較詳解

為什么用 PureComponent

PureComponent 是優化 React 應用程序最重要的方法之一,易于實施,只要把繼承類從 Component 換成 PureComponent 即可,可以減少不必要的 render 操作的次數,從而提高性能,而且可以少寫 shouldComponentUpdate 函數,節省了點代碼。

原理

當組件更新時,如果組件的 propsstate 都沒發生改變, render 方法就不會觸發,省去 Virtual DOM 的生成和比對過程,達到提升性能的目的。

具體就是由于PureComponentshouldeComponentUpdate里,實際是對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的淺顯認識。閱讀并借鑒了幾位大神的文章,第一次分享與廣大初學者共同交流進步。努力!奮斗!????

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容