性能優化的方法:算法
重點關注的是,React組件的渲染性能優化。數據庫
React利用Virtual DOM來提升渲染性能,雖然每次頁面更新都是對組件的從新渲染,但並非將以前渲染的內容所有拋棄重來,藉助Virtual DOM,React可以計算出DOM樹最少的修改,這就是React在默認狀況下都渲染很迅速的祕籍。npm
發現浪費的渲染時間,須要在Chrome瀏覽器中安裝React Perf擴展。編程
注意:這裏的浪費是計算Virtual DOM的浪費,而不是訪問DOM樹的浪費。瀏覽器
「過早的優化是萬惡之源」 - 高德納 《計算機編程藝術》緩存
表面上的意思是,除非性能出了問題,否則就不要花時間去優化性能問題。性能優化
真正上的意思是,「咱們應該忘記忽略很小的性能優化,能夠說97%的狀況下,過早的優化是萬惡之源,而咱們應該關心對性能影響最關鍵的那另外的3%的代碼」 - 高德納。數據結構
高德納認爲,不要將性能優化的經歷浪費在對總體性能提升不大的代碼,而是對性能有關鍵的影響的部分,優化並不嫌早。函數
「過早的優化」:沒有任何量化證據的狀況下,開發者對性能優化的猜想,並無可測量的性能指標,就徹底不知道當前的性能瓶頸在何處,完成優化以後,也沒法知道性能優化是否達到了預期的結果。性能
shouldComponentUpdate的默認實現是一種兜底的保險方法。可是要達到更好的性能,有必要定義shouldComponentUpdate函數。
React-Redux經過提供更好的shouldComponentUpdate的實現方式:
在對比prop和上次渲染所用prop方面上看,依然使用的是「淺層比較」(shallow compare)。能夠簡單的理解爲,JS 的「===」操做符。
想讓React-Redux認爲先後的對象prop是相同的,就必須保證prop指向的JS對象是一致的。
和單個React組建的生命週期同樣,多個React組件也要考慮三個階段:裝載、更新、卸載。
裝載階段:
React組件往下的全部子組件,都須要完全渲染一次,都要經歷一遍React組件的裝載生命週期。所以,沒有多少性能優化的東西。
卸載階段:
只有一個生命週期函數componentWillUnmount,只是清理componentDidMount,作的事情比裝載階段還少,也沒有什麼可優化的空間。
值得關心的過程,只剩下更新階段。
在裝載的過程當中,React經過render方法在內存中產生了一個樹形結構,樹上每個節點表明一個React 組件或者原生DOM元素,這個樹形結構就是所謂的Virtual DOM。React根據這個Virtual DOM 來渲染瀏覽器的DOM樹。
Reconciliation(調和):
在更新階段巧妙地原有的Virtual DOM和新生成的Virtual DOM,找出二者的不一樣之處。根據不一樣來修改DOM樹,更新中這個「找不一樣」的過程就叫作Reconciliation(調和)。
Reconciliation算法:
當React要對比兩個Virtual DOM的樹形結構時,從根節點開始遞歸往下對比,在這個樹形結構中,每一個節點均可以看做當前節點如下部分子樹的根節點。因此這個對比算法能夠從Virtual DOM上任何一個節點開始執行。
React首先檢查兩個樹形的根節點的類型是否相同,根據相同或者不一樣有不一樣處理方式。
若是樹形結構根節點類型不相同,直接認爲原來的樹形結構已經沒有用,須要構建新的DOM樹。原有的樹形上React組件會經歷「卸載」的生命週期。這種方式可能形成某種程度的浪費,可是爲了不較大的複雜度,React必須選擇一個更簡單跟快捷的算法。
也就是說,對於Virtual DOM樹這是一個「更新」的過程,可是卻可能引起這個樹結構上某些組件的「裝載」和「卸載」過程。
節點類型相同的狀況
若是兩個樹形結構的根節點類型相同,React就認爲原來的根節點只須要更新過程,不會將其卸載,也不會引起根節點的從新裝載。
區分節點的類型:
React會保留節點對應的DOM元素,對樹形根節點上的屬性和內容作對比,只更新修改的部分。
React組件,利用React庫定製的類型;
React根據新節點的props去更新原來根節點的組件實例,引起組件實例的更新過程,也就是按照順序引起下列函數:
在這個過程當中,若是shouldComponentUpdate函數返回false,那麼更新過程中止。爲了保持最大的性能,每一個人React組件必需要重視shouldConponentUpdate,若是發現沒有必要從新渲染,那麼直接返回false。 在處理完根節點的對比以後,React的算法會對根節點的每一個子節點重複同樣的動做,這時候每一個子節點就成爲它所覆蓋部分的根節點,處理方式和它的父節點徹底同樣。
當一個組件包含多個子組件的狀況,React的處理方式也很是直接。
React選擇了看起來很傻的辦法,不是尋找兩個序列的精確差異,而是直接挨個比較每一個子組件。
若是在代碼中明確地告訴每一個組建的惟一標識,就能夠幫助React在處理這個問題時聰明不少,告訴每一個組件「身份證號」的途徑就是key屬性。
在一列子組件中,每一個子組件的key值必須惟一,否則就沒有幫助React區分各個組件的身份。
若是key值不惟一,就會誤導React作出錯誤的判斷,甚至致使錯誤的渲染結果。
注意:雖然key是個prop,可是接受能夠的組件並不能讀取到key的值,由於key和ref是React保留的兩個特殊prop,並無預期讓組件直接訪問。
reselect庫的工做原理:只要相關狀態沒有改變,那就直接使用上一次的緩存結果。
reselect的計算過程分爲兩個步驟:
使用reselect須要安裝對應的npm包:
$ npm install --save reselect
Redux要求每一個reducer不能修改state狀態,若是要返回一個新的狀態,就必須返回一個新的對象。
Redux的狀態樹應該設計的儘可能扁平,使用reselect以後,狀態樹的設計應該儘可能範式化(Normalized)。
範式化:就是遵守關係型數據庫的設計原則,減小冗餘數據。
範式化的數據結構就是要讓一份數據只存儲一份,數據冗餘形成的後果就是難以保證數據一致性。
反範式化是利用數據冗餘來換取讀寫效率。
反範式化數據結構的特色就是讀取容易,修改比較麻煩。
對比反範式化和範式化方式的優劣,不能看出範式化更加合理。由於雖然join數據須要花費計算時間,可是應用reselect以後,大部分狀況下都會命中緩存,實際也就是沒有花費不少計算時間。