React組件性能優化

React: 一個用於構建用戶界面的JAVASCRIPT庫.html

React僅僅專一於UI層;它使用虛擬DOM技術,以保證它UI的高速渲染;它使用單向數據流,所以它數據綁定更加簡單;那麼它內部是如何保持簡單高效的UI渲染呢?react

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

就是說React在接收到props或者state更新時,React就會經過前面的方式更新UI。就算從新使用ReactDOM.render(<Component />, mountNode),它也只是看成props更新,而不是從新掛載整個組件。因此React整個UI渲染是比較快的。github

可是,這裏面有幾個問題

1. 若是更新的props和舊的同樣,這個時候很明顯UI不會變化,可是React仍是要進行虛擬DOM的diff,這個diff就是多餘的性能損耗,並且在DOM結構比較複雜的狀況,整個diff會花費較長的時間。算法

2. 既然React老是要進行虛擬DOM的diff,那麼它的diff規則是什麼?怎麼利用?瀏覽器

PureRenderMixin

針對第一個問題React給咱們提供了 PureRenderMixin。
若是React組件是純函數的,就是給組件相同的props和state組件就會展示一樣的UI,可使用這個Minxin來優化React組件的性能。性能優化

var PureRenderMixin = require('react-addons-pure-render-mixin');
React.createClass({
      mixins: [PureRenderMixin],

      render: function() {
        return <div className={this.props.className}>foo</div>;
      }
});

ES6中的用法是數據結構

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

      render() {
        return <div className={this.props.className}>foo</div>;
      }
}

PureRenderMixin的原理就是它實現了shouldComponentUpdate,在shouldComponentUpdate內它比較當前的props、state和接下來的props、state,當二者相等的時候返回false,這樣組件就不會進行虛擬DOM的diff。框架

這裏須要注意:
PureRenderMixin內進行的僅僅是淺比較對象。若是對象包含了複雜的數據結構,深層次的差別可能會產生誤判。僅用於擁有簡單props和state的組件。dom

shouldComponentUpdate

React雖然提供簡單的PureRenderMixin來提高性能,可是若是有更特殊的需求時怎麼辦?若是組件有複雜的props和state怎麼辦?這個時候就可以使用shouldComponentUpdate來進行更加定製化的性能優化。

boolean shouldComponentUpdate(object nextProps, object nextState) {
    return nexprops.id !== this.props.id;
}

在React組件須要更新以前就會調用這個方法,若是這個方法返回false,則組件不更新;若是返回true,則組件更新。在這個方法內部能夠經過nextProps和當前props,nextState和當前state的對比決定組件要不要更新。

若是對比的數據結構比較複雜,層次較深,對比的過程也是會有較大性能消耗,又可能得不償失。
這個時候immutable.js就要登場了,也是fb出品,有人說這個框架的意義不亞於React,可是React光芒太強。它能解決複雜數據在deepClone和對比過程當中性能損耗。

注意:shouldComponentUpdate在初始化渲染的時候不會調用,可是在使用forceUpdate方法強制更新的時候也不會調用。

render

PureRenderMixin和shouldComponentUpdate的關注點是UI需不須要更新,而render則更多關注虛擬DOM的diff規則了,如何讓diff結果最小化、過程最簡化是render內優化的關注點。

React在進行虛擬DOM diff的時候假設:

一、擁有相同類的兩個組件將會生成類似的樹形結構,擁有不一樣類的兩個組件將會生成不一樣的樹形結構。
二、能夠爲元素提供一個惟一的標誌,該元素在不一樣的渲染過程當中保持不變。

DOM結構 

renderA: <div />
renderB: <span />
=> [removeNode <div />], [insertNode <span />

DOM屬性

renderA: <div id="before" />
renderB: <div id="after" />
=> [replaceAttribute id "after"]

以前插入DOM

renderA: <div><span>first</span></div>
renderB: <div><span>second</span><span>first</span></div>
=> [replaceAttribute textContent 'second'], [insertNode <span>first</span>]

以前插入DOM,有key的狀況

renderA: <div><span key="first">first</span></div>
renderB: <div><span key="second">second</span><span key="first">first</span></div>
=> [insertNode <span>second</span>]

因爲依賴於兩個預判條件,若是這兩個條件都沒有知足,性能將會大打折扣。

一、diff算法將不會嘗試匹配不一樣組件類的子樹。若是發現正在使用的兩個組件類輸出的 DOM 結構很是類似,你能夠把這兩個組件類改爲一個組件類。

二、若是沒有提供穩定的key(例如經過 Math.random() 生成),全部子樹將會在每次數據更新中從新渲染。

總結

使用PureRenderMixin、shouldComponentUpdate來避免沒必要要的虛擬DOM diff,在render內部優化虛擬DOM的diff速度,以及讓diff結果最小化。

使用immutable.js解決複雜數據diff、clone等問題。

參考

immutable.js

reconciliation

pure-render-mixin

相關文章
相關標籤/搜索