一、單個React組件的性能優化
React利用Virtual DOM來提高渲染性能,雖然這能將每次DOM操作量減少到最小,計算和比較Virtual DOM依然是一個復雜的計算過程。如果能夠在計算Virtual DOM之前就能判斷渲染結果不會有變化,那樣可以干脆不要進行Virtual DOM計算和比較,速度就會更快。
發現浪費的渲染時間
在Chrome瀏覽器中安裝React Perf擴展,步驟省略(屬于操作部分)
性能優化的時機
“我們應該忘記忽略很小的性能優化,可以說97%的情況下,過早的優化是萬惡之源,而我們應該關心對性能影響最關鍵的那另外3%的代碼” --高德納
對于合并多個字符串,怎樣合并,使用什么方法合并不大可能對整個應用造成關鍵的性能影響,這就是高納德所說的97%的情況,而選擇用什么樣的方式去定義組件的接口,如何定義state到prop的轉變,使用什么樣的算法來比對Virtual DOM,這些決定對性能和架構的影響是巨大的,就是那關鍵的3%。
二、多個React組件的性能優化
和單個組件的生命周期一樣,React組件也要考慮3個階段:裝載階段、更新階段、卸載階段。其中,裝載階段基本沒什么可以優化的空間,因為這部分工作沒有什么可以省略的。而卸載階段,只有一個生命周期函數componentWillUnmount,這個函數做的事情只是清理componentDidMount添加的事件處理監聽等收尾工作,做的事情要比裝載過程少很多,所以也沒什么可以優化的空間。所以值得關注的過程,只剩下更新過程。
React的調和過程
React在更新階段,很巧妙的對比原有的Virtual DOM和新生成的Virtual DOM(存在于內存中),找出兩者的不同,根據不同修改DOM樹,這樣只需做最小的必要改動。
React在更新中找不同的過程,就叫做調和(Reconciliation)。
React實際采用的算法的時間復雜度是O(N)。React的Reconciliation算法并不復雜,當React要對比兩個Virtual DOM的樹形結構的時候,從根節點開始遞歸往下對比,在樹形結構上,每個節點都可以看做這個節點以下子樹部分的根節點,所以其實這個對比算法可以從Virtual DOM上的任何一個節點開始執行。
React首先檢查兩個根節點的類型是否相同,根據相同或者不同有不同處理方式。
(1)節點類型不同的情況
這時可以直接認為原來的樹形結構已經沒用,需要重新構建新的DOM樹,原有樹形上的React組件會經歷“卸載”的生命周期。這時,componentWillUnmount的方法會被調用,取而代之的組件則會經歷裝載過程的生命周期,組件的componentWillMount、render和componentDidMount方法會被依次調用。
(2)節點類型相同的情況
這時React就會認為原來的根節點只需要更新,不必將其卸載,也不會引發根節點的重新裝載。
這時,有必要區分一下節點的類型,節點的類型可以分為兩類:一類是DOM元素類型,對應的就是HTML直接支持的元素類型,比如<div />,<span />和<p />;另一類是React組件,也就是利用React庫定制的類型。
三、用reselect提高數據獲取性能
兩階段選擇過程
reselect庫的工作原理:只要相關狀態沒有改變,那就直接使用上一次的緩存結果。
reselect庫被用來創造“選擇器”,即接受state作為參數的函數,這個選擇器函數返回的數據就是我們某個mapStateToProps需要的結果。
reselect認為一個選擇器的工作可以分為兩個部分,把一個計算步驟分為兩個步驟:
(1)從輸入參數state抽取第一層結果,將這第一層結果和之前抽取的第一層結果做比較,如果發現完全相同,就沒有必要進行第二部分運算了,選擇器直接把之前第二部分的運算結果返回就好了。注意:這一部分做的比較,就是JavaScript的===操作符比較,如果第一層結果是對象的話,只有是同一對象才會被認為是相同。
(2)根據第一層結果計算出選擇器需要返回的最終結果。
顯然,每次選擇器函數被調用時,步驟一都會被執行,但步驟一的結果被用來判斷是否可以使用緩存的結果,所以并不是每次都會調用步驟二的運算。
剩下的事情就是確定選擇器步驟一和步驟二分別進行什么運算。原則很簡單,步驟一運算因為每次選擇器都要使用,所以一定要快,運算要非常簡單,最好就是一個映射運算,通常就只是從state參數中得到某個字段的引用就足夠,把剩下來的重活累活都交給步驟二去做。
在TodoList的具體例子中,todos和filter的值直接決定了應該顯示什么樣的待辦事項,所以,步驟一是獲取todos和filter的值,步驟二是根據這兩個值進行計算。
使用reselect需要安裝對應的npm包:
npm install --save reselect
reselect提供了創造選擇器的createSelector函數,這是一個高階函數,也就是接受函數為參數來產生一個新函數的函數。
第一個參數是一個函數數組,每個元素代表了選擇器步驟一需要做的映射計算,這里我們提供了兩個函數getFilter和getTodos,代碼如下:
const getFilter = (state)=>state.filter;
const getTodos = (state) = >state.todos;
createSelector函數的第二個參數代表步驟二的計算過程,參數為第一個參數的輸出結果。
現在,可以在TodoList模塊中改用新定義的選擇器來獲取待辦事項數據了:
import {selectVisibleTodos} from '../selector.js'
const mapStateToProps = (state) =>{
return {
todos : selectVisibleTodos(state)
}
}
Redux要求每個reducer不能修改state狀態,如果要返回一個新的狀態,就必須返回一個新的對象。這樣,如果state狀態樹上的某個節點沒有變化,那我們可以認為這個節點下的數據沒有改變,應用在reselect中,步驟一的運算就可以確定使用緩存的數據結果。
雖然reselect的createSelector創造的選擇器并不是一個純函數,但是createSelector接受的所有函數參數都是純函數,雖然選擇器有“記憶”這個副作用,但只要輸入參數state沒有變化,產生的結果也就沒有變化,表現得卻類似于一個純函數。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。