文章同步於Github Pines-Cheng/blogjavascript
React在減小重複渲染方面確實是有一套獨特的處理辦法,那就是虛擬DOM,但顯然在首次渲染的時候React絕無可能超越原生的速度,或者必定能將其它的框架比下去。尤爲是在優化前的React,每次數據變更都會執行render,大大影響了性能,特別是在移動端。html
在初始化渲染時,咱們須要渲染整個應用
(綠色 = 已渲染節點)
java
咱們想更新一部分數據。這些改變只和一個葉子節點相關(綠色的)react
咱們只想渲染通向葉子節點的關鍵路徑上的這幾個節點(綠色的)
git
若是你不告訴 React 別這樣作,它便會如此
(橘黃色 = 浪費的渲染)程序員
從上圖能夠看見,組件除了必要渲染的三個節點外,還渲染了其餘沒必要要渲染的節點,這對性能是一個很大的浪費。若是對於複雜的頁面,這將致使頁面的總體體驗效果很是差。所以要提升組件的性能,就應該想盡一切方法減小沒必要要的渲染。github
React的生命週期以下,還沒熟悉的同窗能夠去熟悉一下。
web
由於其中的 shouldComponentUpdate
是優化的關鍵。React的重複渲染優化的核心其實就是在shouldComponentUpdate裏面作數據比較。在優化以前,shouldComponentUpdate
是默認返回true的,這致使任什麼時候候觸發任何的數據變化都會使component從新渲染。這必然會致使資源的浪費和性能的低下——你可能會感受比較原生的響應更慢。segmentfault
React性能優化的關鍵在於shouldComponentUpdate
,
api
在上面的示例中,由於 C2 的 shouldComponentUpdate
返回 false,React 就不須要生成新的虛擬 DOM,也就不須要更新 DOM,注意 React 甚至不須要調用 C4 和 C5 的 shouldComponentUpdate
。
C1 和 C3 的 shouldComponentUpdate
返回 true,因此 React 須要向下到葉子節點檢查它們,C6 返回 true,由於虛擬 DOM 不相等,須要更新 DOM。最後感興趣的是 C8,對於這個節點,React 須要計算虛擬 DOM,可是由於它和舊的相等,因此不須要更新 DOM。
在傳入組件的props和state只有一層時,咱們能夠直接使用 React.PureComponent,它會自動幫咱們進行淺比較(shallow-compare),從而控制shouldComponentUpdate的返回值。
可是,當傳入props或state不止一層,或者未array和object時,淺比較(shallow-compare
)就失效了。固然咱們也能夠在 shouldComponentUpdate()
中使用使用 deepCopy
和 deepCompare
來避免無必要的 render()
,但 deepCopy
和 deepCompare
通常都是很是耗性能的。這個時候咱們就須要 Immutable
。
JavaScript 中的對象通常是可變的(Mutable),由於使用了引用賦值,新的對象簡單的引用了原始對象,改變新的對象將影響到原始對象。如
foo={a: 1}; bar=foo; bar.a=2
你會發現此時 foo.a 也被改爲了 2。雖然這樣作能夠節約內存,但當應用複雜後,這就形成了很是大的隱患,Mutable 帶來的優勢變得得不償失。爲了解決這個問題,通常的作法是使用 shallowCopy
(淺拷貝)或 deepCopy
(深拷貝)來避免被修改,但這樣作形成了 CPU 和內存的浪費。
而Immutable 能夠很好地解決這些問題。
Immutable Data
就是一旦建立,就不能再被更改的數據。對 Immutable 對象的任何修改或添加刪除操做都會返回一個新的 Immutable 對象。Immutable 實現的原理是 Persistent Data Structure
(持久化數據結構),也就是使用舊數據建立新數據時,要保證舊數據同時可用且不變。同時爲了不 deepCopy 把全部節點都複製一遍帶來的性能損耗,Immutable 使用了 Structural Sharing
(結構共享),即若是對象樹中一個節點發生變化,只修改這個節點和受它影響的父節點,其它節點則進行共享。
能夠看看下面這個經典的動畫:
Immutable.js本質上是一個JavaScript的持久化數據結構的庫 ,可是因爲同期的React太火,而且和React在性能優化方面完美無缺的配合,致使你們經常把它們二者綁定在一塊兒。
Immutable.js是Facebook 工程師 Lee Byron 花費 3 年時間打造,但沒有被默認放到 React 工具集裏(React 提供了簡化的 Helper)。它內部實現了一套完整的 Persistent Data Structure
,且數據結構和方法很是豐富(徹底不像JS出身的好很差)。像 Collection、List、Map、Set、Record、Seq。有很是全面的map、filter、groupBy、reduce、find函數式操做方法。同時 API 也儘可能與 Object 或 Array 相似。 Immutable.js 壓縮後下載有 16K。
其中有 3 種最重要的數據結構說明一下:(Java 程序員應該最熟悉了)
Map
:鍵值對集合,對應於 Object,ES6 也有專門的 Map 對象
List
:有序可重複的列表,對應於 Array
Set
:無序且不可重複的列表
簡單示例
import { Map } from "immutable"; const map1 = Map({ a: 1, b: 2, c: 3 }); const map2 = map1.set('b', 50); map1.get('b'); // 2 map2.get('b'); // 50
seamless-immutable是另外一套持久化數據結構的庫,它並無實現完整的 Persistent Data Structure
,而是使用 Object.defineProperty
(所以只能在 IE9 及以上使用)擴展了 JavaScript 的 Array 和 Object 對象來實現,只支持 Array 和 Object 兩種數據類型,API 基於與 Array 和 Object ,所以許多不用改變本身的使用習慣,對代碼的入侵很是小。同時,它的代碼庫也很是小,壓縮後下載只有 2K。
簡單示例
// 使用 seamless-immutable.js 後 import Immutable from 'seamless-immutable'; var array = Immutable(["totally", "immutable", {hammer: "Can’t Touch This"}]); array[1] = "I'm going to mutate you!" array[1] // "immutable" array[2].hammer = "hm, surely I can mutate this nested object..." array[2].hammer // "Can’t Touch This" for (var index in array) { console.log(array[index]); } // "totally" // "immutable" // { hammer: 'Can’t Touch This' } JSON.stringify(array) // '["totally","immutable",{"hammer":"Can’t Touch This"}]'
seamless-immutable的實現依賴於ECMAScript 5 的一些特性,如Object.defineProperty 和 Object.freeze,所以會在瀏覽器兼容性方面有所欠缺:
不過這不是問題啦,可使用 polyfill es-shims/es5-shim 來解決。
雖然 Immutable.js
儘可能嘗試把 API 設計的原生對象相似,有的時候仍是很難區別究竟是 Immutable 對象仍是原生對象,容易混淆操做。
Immutable 中的 Map 和 List 雖對應原生 Object 和 Array,但操做很是不一樣,好比你要用 map.get('key')
而不是 map.key
,array.get(0)
而不是 array[0]
。另外 Immutable 每次修改都會返回新對象,也很容易忘記賦值。
當使用外部庫的時候,通常須要使用原生對象,也很容易忘記轉換。
固然也有一些辦法來避免相似問題發生:
使用 Flow 或 TypeScript 這類有靜態類型檢查的工具
約定變量命名規則:如全部 Immutable 類型對象以 $$ 開頭。
使用 Immutable.fromJS
而不是 Immutable.Map
或 Immutable.List
來建立對象,這樣能夠避免 Immutable 和原生對象間的混用。
可是還有一個致命的問題是,對現有代碼的改造,使用 Immutable.js 成本實在太大。
而seamless-immutable
雖然數據結構和API不如Immutable.js
豐富,可是對於只想使用Immutable Data來對React進行優化以免重複渲染的咱們來講,已是綽綽有餘了。並且Array和Object原生的方法等均可以直接使用,原有項目改動極小。
因爲seamless-immutable的實現依賴於ECMAScript 5 和原生的Array、Object自然的兼容性,致使其在React中的使用很是簡單,只要注意三點就能夠達到效果:
初始化state數據的時候,使用Immutable的初始化方式。
import Immutable from 'seamless-immutable'; state: { orderList: Immutable([]), }
修改state數據的時候,一樣也要注意:
saveOrderList(state, {payload: items}) { return {...state, orderList: Immutable(items)}; }
使用pure-render-decorator,真是方便、快捷又優雅。固然,因爲decorator屬於ES7的特性,babel還須要本身配置。
import React from 'react'; import pureRender from 'pure-render-decorator'; @pureRender class OrderListView extends React.Component { render() { const {orderList} = this.props; return ( <div> { orderList.map((item) => { return ( <div key={item.orderNum}> <div>{item.orderNum}</div> <div>{item.createTime}</div> <div>{item.contact}</div> <hr/> </div> ); }) } </div> ); } } export default OrderListView;
怎麼樣,傳說中的React的SCU的優化就是這麼簡單,趕忙去試試吧。