React組件性能調優

React是一個專一於UI層的框架,它使用虛擬DOM技術,以保證它UI的高速渲染;使用單向數據流,所以它數據綁定更加簡單;那麼它內部是如何保持簡單高效的UI渲染呢?這種渲染機制有可能存在什麼性能問題呢?javascript

原文連接120java

React組件渲染問題引出

React不直接操做DOM,它在內存中維護一個快速響應的DOM描述,render方法返回一個DOM的描述,React可以計算出兩個DOM描述的差別,而後更新瀏覽器中的DOM。這就是著名的DOM Diffreact

就是說React在接收到屬性(props)或者狀態(state)更新時,就會經過前面的方式更新UI。因此React整個UI渲染是比較快的。可是這裏面可能出現的問題是:git

假設咱們定義一個父組件,其包含了5000個子組件。咱們有一個輸入框輸入操做,每次輸入一個數字,對應的那個子組件背景色變紅。github

 

<Components> <Components-1 /> <Components-2 /> <Components-3 /> ... <Components-5000 /> </Components>

這樣咱們輸入數字1,則子組件1背景色變化,可是在這個過程當中,全部的子組件都進行了從新渲染,致使總體渲染變慢。形成這種現象的緣由是 React中父組件更新默認觸發全部子組件更新。(具體例子見文章後demo連接)瀏覽器

同時,咱們常常在遍歷列表元素時候會遇到這樣的提示:性能優化

 

Warning: Each child in an array or iterator should have a unique "key" prop.

這就是咱們今天要討論的兩個性能優化點:數據結構

  1. 父組件更新默認觸發全部子組件更新
  2. 列表類型的組件默認更新方式很是複雜

React性能檢測工具

咱們利用 react-addons-perf 進行性能檢測。引入方法以下:app

 

import Perf from 'react-addons-perf' window.Perf = Perf // 掛載到全局變量方便使用

檢測方法,在瀏覽器控制檯輸入以下命令:框架

  • 開始記錄:Perf.start()
  • 結束記錄:Perf.stop()
  • 打印結果:printInclusive()

 

 

控制檯會以表格的形式展現出結果:

 

 

上圖記錄了每一個組件的執行耗時,渲染次數等關鍵信息。咱們能夠有針對性的進行優化。

注意:生產環境不要引入Perf

React 性能優化原理

這是React官網對組件渲染機制的描述圖,其中綠色組件表明不須要更新,紅色組件須要更新,影響更新的條件主要有SCU(shouldComponentUpdate)及DOM DIff結果。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

咱們再來看看 組件觸發更新的流程圖

 

經過上述流程圖,再對比渲染的圖解能夠看到,React的性能瓶頸主要出如今生成DOM及DOM Diff的過程。若是進行性能優化,關鍵在:

  • shouldComponentUpdate 階段判斷,若是屬性及狀態與上一次相同,這個時候很明顯UI不會變化,也不須要執行後續生成DOM,DOM Diff的過程了,能夠提升性能。
  • DOM Diff 階段優化,提升Diff的效率

如何提升組件的渲染效率

針對文章開頭提出的兩個性能問題,咱們獲得如下解決方案:

  • 子組件執行 shouldComponentUpdate 方法,自行決定是否更新
  • 給列表中的組件添加key屬性

咱們能夠經過控制子組件的 shouldComponentUpdate 從而控制是否渲染:

 

// 接收兩個參數,分別爲待更新的屬性及狀態值 shouldComponentUpdate(nextProps, nextState) { // 若是當前的value值與待更新不相等,才執行更新 return this.props.value !== nextProps.value; }

針對列表遍歷類型,遍歷時候增長惟一 key 屬性值,對子組件進行惟一性識別,準確知道要操做的子組件,提升 DOM Diff 的效率。

 

array.map(val, key) => {
   return <span key={key}>{val}</span> })

PureRenderMixin 與 PureComponent

爲了提升React組件渲染性能,React 針對組件的 shouldComponentUpdate 方法進行了封裝處理,咱們不須要在每一個組件裏面手動編寫 shouldComponentUpdate

PureRenderMixin

React在以前版本提供了 PureRenderMixin 的mixin形式,其用法以下:

 

// react官方demo import PureRenderMixin from 'react-addons-pure-render-mixin'; class FooComponent extends React.Component { constructor(props) { super(props); this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); }

其原理就是重寫了 shouldComponentUpdate 方法。

PureComponent

React 15.3.0 新增了一個 PureComponent 類,以 ES2015 class 的方式方便地定義純組件 (pure component),用於取代以前的 PureRenderMixin

這個類的用法很簡單,若是你有些組件是純組件,那麼把繼承類從 Component 換成 PureComponent 便可。當組件更新時,若是組件的 props 和 state 都沒發生改變,render 方法就不會觸發,省去 Virtual DOM 的生成和比對過程,達到提高性能的目的。

 

import React, { PureComponent } from 'react' class Example extends PureComponent { render() { // ... } }

這裏要注意的是:PureRenderMixin、PureComponent 內進行的僅僅是淺比較對象(shallowCompare)。若是對象包含了複雜的數據結構,深層次的差別可能會產生誤判。好比,若是咱們的state變爲:

 

state = {
     value: { foo: 'bar' } } // 每次更改value值的時候進行: this.setState({ value: newValue });

此時直接經過值的比較是行不通的,由於對象的引用關係,致使在子組件裏面接受到的 this.props.value 與 nextProps.value 永遠都是相等的。這裏的解決方案主要有:

  • 深比較: 原理與深拷貝相似,比較耗時,不推薦
  • immutable.js:FaceBook官方提出的不可變數據解決方案,主要解決了複雜數據在deepClone和對比過程當中性能損耗

總結

雖然React提供了Virtual DOM DOM Diff 等優秀的能力來提升渲染性能,可是在實際使用過程當中,咱們常常會遇到父組件更新,不須要更新因此子組件的場景(分頁),此時必須考慮利用React本週的渲染機制來進行優化。

相關文章
相關標籤/搜索