React性能提高

瞭解react如何提高性能將有助於咱們更好的編寫代碼。我的認爲react中不少的性能優化,其實都是圍繞着react的核心diff算法來展開的,經過優化,減小diff算法中一些沒必要要的步驟,從而來提升性能。下面是我平時開發總結出來的一些經驗。react

Component與PureComponent

通常來說,不少人都是使用的Component,可是會帶來一個問題,那就是在父組件的props或者state發生改變的時候,或者說的再準確一點,每當咱們調用setState或者props發生改變的時候,都會再執行一次render,會從新生成virtualdom,這個時候,好比說在該父組件中還存在HomeArea兩個組件,也會從新進行渲染(執行render -> 生成virtualDom -> 對比新舊virtualDom生成差別對象 -> 將生成的差別對象應用到真實dom上),可是若是HomeArea沒有任何改變那麼差別對象不包含任何東西,那麼就至關於從新渲染的時候前三步耗費了性能。算法

這種性能耗費實際上是不必的, 因而react提供了兩種方法給咱們來優化這個過程。redux

Component中shouldComponentUpdate方法

該方法在Component中沒有繼承實現,須要咱們手動實現。而且在初次渲染和調用forceUpdate的時候不觸發數組

  • shouldComponentUpdate(nextProps, nextState)

若是咱們要判斷該組件是否更新必須經過比較this.propsnextProps加上this.statenextState, 判斷他們是否改變,由於默認是返回true的。若是改變了能夠經過返回true來告訴react,須要進行前面說到的從新渲染的過程,否者返回false。那麼shouldComponentUpdate後面的生命週期render() -> getSnapshotBeforeUpdate() -> componentDidUpdate()都不會執行。這樣就達到了提升性能的做用。性能優化

可是須要注意的一點是,雖然該組件沒有被re-rendering,可是若是該組件中還包含子組件,若是此時子組件的state(這裏我沒有說還有props)發生了改變,此子組件仍是會被從新渲染,並非說父組件的shouldComponentUpdate返回false,全部的子組件就不會從新渲染了。你可能如今會有點迷糊,不是說返回false就不會從新渲染也不會執行render的嗎? 那怎麼會從新渲染子組件呢? 讓咱們來想想:若是經過比較發現該組件的stateprops都沒有發生改變,說明傳給子組件的props確定沒有變,此時惟一可能改變的就是子組件自生的state。在開發中可能會出現這種狀況,即父組件狀態沒有改變,可是子組件自身的state發生改變。 並且在實際項目開發中,結合使用redux來管理數據的時候,若是父組件的狀態沒有發生改變,子組件其實可能仍是會受到props的影響的。數據結構

還有一點是在比較的過程當中,若是過於複雜也會致使過多的性能損耗,還不如不進行比較,進行默認的步驟。例如深度遍歷、JSON.stringify這種耗費大量的性能的操做。一般比較推薦的作法是進行淺比較,而且下面要介紹的PureComponent也是使用的淺比較,什麼是淺比較在下面PureComponent專題中說。dom

使用PureComponent

PureComponent內部幫咱們實現了shouldComponentUpdate,其餘和Component同樣。可是在shouldComponentUpdate進行的是一個淺比較,看看官方文檔是怎麼說的。函數

clipboard.png
他這裏並無仔細說淺比交究竟是怎麼比較。通過個人調試,我發現淺比較具備如下特色。性能

  1. 對於state只比較第一層,而且若是是基本類型,那麼比較他們的值,若是是引用值,那麼比較他們的引用。
  2. 對於傳遞來的props, 也只比較第一層,規則和上面同樣。

下面給幾個例子:假設state中的數據結構是這樣優化

constructor (props) {
    super(props)
    this.state = {
      count: 0,
      person: {
        // name: 'longjincen'
        person: {
          person: {
            name: 'longjincen'
          }
        }
      }
    }
  }

當咱們繼承的是Component的時候,只要調用setState就會觸發從新渲染,可是當咱們繼承的是PureComponent的時候,就不同了。當咱們像下面這樣改變state的時候

handleClick = () => {
    const { person } = this.state
    person.person.person = {
      name: 'xiaoya'
    }
    this.setState({
      person: person
    })
  }

這個時候最外層的person始終都是同一個引用,因此這個時候是不會觸發從新渲染的,就算咱們內部的person是一個新的引用,由於它只比較第一層。因此當咱們想要更新的時候,第一層返回的必須是一個新的對象,纔會觸發從新渲染。因此官方文檔中也說了,若是數據結構比較複雜,那麼可能會致使一些問題,要麼當你知道改變的時候調用forceUpdate,要麼使用immutable來包裝你的state,immutable是一個js庫,用法和js差很少,是facebook開發出來的,通常和PureComponent搭配使用。

因此當咱們使用PureComponent的時候返回的state必須是一個新的對象,下面給出幾種解決辦法:

  1. 使用Object.assgin({}, xxx), 來建立一個新的對象返回
  2. 使用擴展運算符{ ...xxx }
1.
  handleClick = () => {
    const { person } = this.state
    person.person.person.name = 'xiaoya'
    this.setState({
      person: Object.assign({}, person)
    })
  }
2.
  handleClick = () => {
    const { person } = this.state
    person.person.person.name = 'xiaoya'
    this.setState({
      person: { ...person }
    })
  }

而對於接收到的先後的props的比較和上面介紹的state比較同樣,可是須要注意的一種狀況是,若是咱們將上面的第三層的person對象傳給子組件,那麼在子組件中,因爲接收到的person對象先後引用發生了改變,因此儘管父組件不會觸發從新渲染,子組件仍是會觸發,而若是咱們傳遞的是第二層或者第一層的person因爲引用沒有改變,子組件也不會觸發從新渲染。

最後要說的是在實際項目開發中每每是使用PureComponentimmutable搭配的方式

其餘

無狀態組件

無狀態組件就是使用定義函數的方式來定義組件,這種組件相比於使用類的方式來定義的組件(有狀態組件),少了不少初始化過程,更加精簡,因此要是可使用無狀態組件應當儘量的使用無狀態組件,會大幅度提高效率

不要在render中生成新的引用

定義函數、使用內聯樣式或者動態生成一些不依賴statepropsjsx這些,凡是不依賴stateprops的都應該提到render以外,不然會形成每次render的時候生成新的引用,會致使在diff算法對比屬性或節點的過程當中發現兩個引用不一致,對於react來講,這說明屬性值發生了改變,最後會被替換成新的引用,形成性能浪費。例如函數應該放在render函數外定義,樣式應該單獨建一個文件,而後引入,使用生命週期函數或者自定義函數動態生成一些不依賴statepropsjsx

始終由用戶觸發改變state

render函數中,只能經過特定的用戶事件來觸發state改變,否者容易形成不斷刷新的死循環,像下面這樣就是錯誤的

render () {
    this.setState({
        name: 'xcacsa'
    })
}

始終使用key值

避免使用數組下標做爲key值, 應該確保惟一。在diff算法中,key值用來保證當列表中節點順序改變的時候節點的複用,而不是所有替換。

相關文章
相關標籤/搜索