React 效能優化總結

xiaotianyi發表於2019-04-22

React 效能優化總結

總結了以下幾個方面在react上的效能優化

  • 常見的效能問題場景

  • 時刻注意程式碼的潛在效能問題

  • 注意可重構的程式碼,元件化

  • 瞭解如何使用工具定位效能問題

常見的效能問題場景

  • JavaScript 語言採用的是單執行緒模型,也就是說,所有任務只能在一個執行緒上完成,一次只能做一件事。如果頁面比較複雜,新增了大量的計算,並且還新增了Canvas(Canvas 是一個非常受歡迎的表現方式,同時也是WebGL的入口。它能繪製圖形,圖片,展示動畫,甚至是處理視訊內容)的繪製,那頁面載入可能會卡頓,有可能會呈現出假死狀態。


    解決方案:在Worker中使用OffscreenCanvas或者將頁面渲染時大量的計算放在Worker中。 首先我們先了解幾個概念(以在Worker中用OffscreenCanvas為列)

    • Workers 是一個Web版的執行緒——它允許你在幕後執行你的程式碼。將你的一部分程式碼放到Worker中可以給你的主執行緒更多的空閒時間,這可以提高你的使用者體驗度

    • OffscreenCanvas並不依賴DOM。

      1. 一種是在 Worker 執行緒建立一個 OffscreenCanvas 做後臺渲染,然後再把渲染好的緩衝區 Transfer 回主執行緒顯示。
      2. 一種是主執行緒從當前 DOM 樹中的 Canvas 元素產生一個 OffscreenCanvas,再把這個 OffscreenCanvas 傳送給 Worker 執行緒進行渲染,渲染的結果直接 Commit 到瀏覽器的 Display Compositor 輸出到當前視窗,相當於在 Worker 執行緒直接更新 Canvas 元素的內容。
    • OffscreenCanvas學習文件
      另外web workers學習文件請去官網瞭解

      總結: 學習成本比較低,在耗效能的計算和渲染放在Worker中,確實能提升使用者體驗度

  • dom節點層次多,而且深,更改state或者更改redux,導致與該資料無相關的dom節點多次render。

  • js處理資料過於複雜。定義的狀態資料層次過於深。導致對比或者遍歷資料消耗效能。

時刻注意程式碼的潛在效能問題

  • {...this.props} 不要濫用,請只傳遞component需要的props,傳得太多,或者層次傳得太深,都會加重shouldComponentUpdate裡面的資料比較負擔。

  • 方法繫結的使用

    1. 方法直接繫結在dom節點中
    <div onClick={this.tap.bind(this)} /> 
    複製程式碼
    1. 方法繫結放在constructor中
    constructor(props) {
        super(props);
        this.tap= this.tap.bind(this);
    }
    複製程式碼
    1. 箭頭函式
    tap = ()=>{};
    <div onClick={this.tap} />
    複製程式碼
    tap =(value)=> {};
    <div onClik={()=>this.tap(value)} />
    複製程式碼

    總結:
    1.由於繫結是在render中執行,而render是會執行多次的,每次bind和箭頭函式都會產生一個新的函式,因而帶來了額外的開銷
    2.使用構造器bind的方法,每個例項都可以有效地共享函式體,從而更加有效的使用記憶體。但當要繫結的函式較多時,這種方法又顯得相對的枯燥和無聊。所以,在知道例項不多,函式體不大的前提下,使用箭頭函式更加快捷。
    3.綜合三種寫法,第三種是目前最優寫法

  • 陣列遍歷map

    map裡面新增key,並且key不要使用index(可變的),儘量使用穩定常量作為key。使用index作為key,只是會讓程式碼不報錯,其他一無是處。
    每當元件的props或state改變時, React會重新建立一個virtual DOM, 與上一個作對比, 如果發現兩個virtual DOM不完全相同, 則React就會做reconcile, 把有差異的地方更新到真實的DOM上。
    使用常量作為key
    複製程式碼
  • 儘量少用不可控的refs、DOM操作。

  • props和state的資料儘可能簡單明瞭,扁平化。便於資料對比,陣列遍歷從而減小帶來的效能消耗。

  • 使用return null而不是CSS的display:none來控制節點的顯示隱藏。保證同一時間頁面的DOM節點儘可能的少。

注意可重構的程式碼,元件化

  • 元件分類
  • 可複用性
  • 足夠細
  • 耦合度低

元件分類

在開發前期可以根據業務場景將元件分類

  • 展示類元件(沒有任何互動,只是純展示資料)
  • 互動類元件(頁面互動操作比較頻繁)
  • 資料類元件(比如dva,redux,基本在搭建框架時已模組化)
  • 高階元件(對原有元件的保護,更利於後續的迭代開發)

可複用性

這裡我們就要說一下有狀態元件和無狀態元件的區別了 就如上所說的展示類元件,這種我們就可以把它歸類為無狀態元件,舉個列子

import React from 'react';

const PicModal = props => {
  const { title = '', content = '', picUrl = '', deviceName = '' } = props;
  return (
    <div>
      <span>title</span>
      <span>content</span>
      <span>deviceName</span>
      {picUrl !== '' ? <img src={picUrl}/> : null}
    </div>
  );
};

export default PicModal;
複製程式碼

這樣我們就可以自己封裝一個modal元件,根據業務場景不斷的迭代優化。 而上面的互動類元件大多數情況下都是有狀態元件,維護自身的狀態值。但是要實現可複用性,元件之間的耦合度肯定是要低的。元件自己控制自己內部的state。這樣setState只用區域性更新檢視。減小效能消耗。
注意:千萬不要在父元件定義state,傳值給子元件。
1.降低元件之間的耦合度
2.便於後續的元件迭代

足夠細

根據業務場景將元件拆分的足夠細。

  • 可讀性強
  • 維護性高
  • 便於複用

耦合度低

下面舉一個移動端的列子,web端大多數都會有這種情況,點選專案id列表中projectId,projectList改變。

React 效能優化總結

解決方案:元件1和元件2是通過projectId進行聯動。如果我們拿到這個需求,首先元件化1和2,元件1通過點選id,在父元件中執行getProjectList方法,從而實現渲染元件2。根據元件1中id的state渲染元件1


這樣父元件與子元件耦合度很低,子元件自己維護自己的狀態值
兄弟元件之間互不影響,通過父元件通訊


如果拆分業務元件的思路不清晰,盲目的將狀態值放在父元件中,這樣耦合度會大大增加,元件的複用性會大大降低。

瞭解如何使用工具定位效能問題

  • chrome外掛 redux devtools
  • chrome外掛 react devtools

相關文章