React是一個專一於UI層的框架,它使用虛擬DOM技術,以保證它UI的高速渲染;使用單向數據流,所以它數據綁定更加簡單;那麼它內部是如何保持簡單高效的UI渲染呢?這種渲染機制有可能存在什麼性能問題呢?javascript
原文連接120java
React不直接操做DOM,它在內存中維護一個快速響應的DOM描述,render方法返回一個DOM的描述,React可以計算出兩個DOM描述的差別,而後更新瀏覽器中的DOM。這就是著名的DOM Diff
。react
就是說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.
這就是咱們今天要討論的兩個性能優化點:數據結構
咱們利用 react-addons-perf
進行性能檢測。引入方法以下:app
import Perf from 'react-addons-perf' window.Perf = Perf // 掛載到全局變量方便使用
檢測方法,在瀏覽器控制檯輸入以下命令:框架
控制檯會以表格的形式展現出結果:
上圖記錄了每一個組件的執行耗時,渲染次數等關鍵信息。咱們能夠有針對性的進行優化。
注意:生產環境不要引入Perf
這是React官網對組件渲染機制的描述圖,其中綠色組件表明不須要更新,紅色組件須要更新,影響更新的條件主要有SCU(shouldComponentUpdate)及DOM DIff結果。
咱們再來看看 組件觸發更新的流程圖
:
經過上述流程圖,再對比渲染的圖解能夠看到,React的性能瓶頸主要出如今生成DOM及DOM Diff的過程
。若是進行性能優化,關鍵在:
針對文章開頭提出的兩個性能問題,咱們獲得如下解決方案:
shouldComponentUpdate
方法,自行決定是否更新咱們能夠經過控制子組件的 shouldComponentUpdate
從而控制是否渲染:
// 接收兩個參數,分別爲待更新的屬性及狀態值 shouldComponentUpdate(nextProps, nextState) { // 若是當前的value值與待更新不相等,才執行更新 return this.props.value !== nextProps.value; }
針對列表遍歷類型,遍歷時候增長惟一 key
屬性值,對子組件進行惟一性識別,準確知道要操做的子組件,提升 DOM Diff 的效率。
array.map(val, key) => {
return <span key={key}>{val}</span> })
爲了提升React組件渲染性能,React 針對組件的 shouldComponentUpdate
方法進行了封裝處理,咱們不須要在每一個組件裏面手動編寫 shouldComponentUpdate
。
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 方法。
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
永遠都是相等的。這裏的解決方案主要有:
雖然React提供了Virtual DOM
DOM Diff
等優秀的能力來提升渲染性能,可是在實際使用過程當中,咱們常常會遇到父組件更新,不須要更新因此子組件的場景(分頁),此時必須考慮利用React本週的渲染機制來進行優化。