React PureComponent 學習及淺比較詳解 (新手秒懂)

爲何用 PureComponent

PureComponent 是優化 React 應用程序最重要的方法之一,易於實施,只要把繼承類從 Component 換成 PureComponent 便可,能夠減小沒必要要的 render 操做的次數,從而提升性能,並且能夠少寫 shouldComponentUpdate 函數,節省了點代碼。javascript

原理

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

具體就是因爲PureComponentshouldeComponentUpdate裏,實際是對props/state進行了一個 淺對比,因此對於嵌套的對象不適用,沒辦法比較出來。react

淺對比??

首先讓咱們來看下 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() 應運而生ui

// 其實是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 代碼this

// 用原型鏈的方法
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 是否二者都有,而且是不是 一個引用,也就是隻比較了 第一層 的值,確實很淺,因此深層的嵌套數據是對比不出來的。spa

案例 (易變數據不能使用一個引用)

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的淺顯認識。閱讀並借鑑了幾位大神的文章,第一次分享與廣大初學者共同交流進步。努力!奮鬥!💪💪

相關文章
相關標籤/搜索