首頁  >  文章  >  web前端  >  react元件拆分的重要性

react元件拆分的重要性

王林
王林轉載
2021-02-20 11:23:352158瀏覽

react元件拆分的重要性

前言:

React 是一個用於建立使用者介面的 JAVASCRIPT 函式庫。主要用於建立UI,很多人認為 React 是 MVC 中的 V(視圖)。

react採用虛擬DOM技術減少Javascript與真正DOM的交互,提升了前端效能;採用單向資料流機制,父元件透過props將資料傳遞給子元件,這樣讓資料流向一目了然。

一旦元件的props或則state改變,元件及其子元件都會重新re-render和vdom-diff,從而完成資料的流向互動。但是這種機制在某些情況下比如說資料量較大的情況下可能會存在一些效能問題。以下就來分析react的效能瓶頸,並用結合react-addons-perf工具來說明react元件拆分的重要性。

react效能瓶頸

要了解react的效能瓶頸,就需要知道react的渲染流程。它的渲染可以分為兩個階段:

初始元件化
該階段會執行元件及其所有子元件的render方法,從而產生第一版的虛擬dom。

元件更新渲染。
元件的props或state任意發生改變就會觸發元件的更新渲染。預設情況下其也會執行該元件及其所有子元件的render方法來取得新的虛擬dom。

我們說的效能瓶頸指的是元件更新階段的情況。

react元件更新流程

透過上面分析可以知道元件更新具體過程如下:

執行該元件及其所有子元件的render方法取得更新後的虛擬DOM ,即re-render,即使子元件無需更新。

然後對新舊兩份虛擬DOM進行diff來進行元件的更新

在這個過程中,可以透過元件的shouldComponentUpdate方法傳回值來決定是否需要re-render。

react的整個更新渲染流程可以藉用一張圖來加以說明:

react元件拆分的重要性

預設地,元件的shouldComponentUpdate回傳true,即React預設會調用所有元件的render方法來產生新的虛擬DOM, 然後跟著舊的虛擬DOM比較來決定元件最終是否需要更新。

react效能瓶頸

借圖說話,例如下圖是一個元件結構tree,當我們要更新某個子元件的時候,如下圖的綠色元件(從根元件傳遞下來應用在綠色元件上的資料改變):

react元件拆分的重要性

理想情況下,我們只希望關鍵路徑上的元件進行更新,如下圖:

react元件拆分的重要性

#但是,實際效果卻是每個元件都完成re-render和virtual-DOM diff過程,雖然元件沒有變更,這明顯是一種浪費。如下圖黃色部分錶示浪費的re-render和virtual-DOM diff。

react元件拆分的重要性

根據上面的分析,react的效能瓶頸主要表現在:

對於props和state沒有變化的元件,react也要重新產生虛擬DOM及虛擬DOM的diff。

用shouldComponentUpdate來進行效能優化

針對react的效能瓶頸,我們可以透過react提供的shouldComponentUpdate方法來做點優化的事,可以有選擇的進行元件更新,從而提升react的效能,具體如下:

shouldComponentUpdate需要判斷目前屬性和狀態是否和上一次的相同,如果相同則不需要執行後續產生虛擬DOM及其diff的過程,否則需要更新。

(學習影片分享:javascript影片教學

具體可以這麼顯示實作:

shouldComponentUpdate(nextProps, nextState){   return !isEqual(nextProps, this.props) || !isEqual(nextState, this.state)
}

其中,isEqual方法為判斷兩個物件是否相等(指的是其物件內容相等,而非全等)。

透過顯示覆蓋shouldComponentUpdate方法來判斷元件是否需要更新從而避免無用的更新,但是若為每個元件添加該方法會顯得繁瑣,好在react提供了官方的解決方案,具體做法:

方案對元件的shouldComponentUpdate進行了封裝處理,實現對元件的目前屬性和狀態與上一次的進行淺對比,從而決定元件是否需要更新。

react在發展的不同階段提供兩套官方方案:

PureRenderMin
一種是基於ES5的React.createClass創建的元件,配合該形式下的mixins方式來組合PureRenderMixin提供的shouldComponentUpdate方法。當然用ES6創建的元件也能使用該方案。

react元件拆分的重要性

PureComponent
該方案是在React 15.3.0版本發布的針對ES6而增加的一個元件基底類別:React.PureComponent。這明顯對ES6方式創建的組件更加友善。

react元件拆分的重要性

需要指出的是,不管是PureRenderMin還是PureComponent,他們內部的shouldComponentUpdate方法都是淺比較(shallowCompare)props和state物件的,也就是只比較物件的第一層的屬性及其值是不是相同。例如下面state物件變更為如下值:

react元件拆分的重要性

因為state的value被賦予另一個對象,使nextState.value與this.props.value始終不等,導致淺比較通過不了。在實際專案中,這種嵌套的物件結果是很常見的,如果使用PureRenderMin或PureComponent方式時起不到應有的效果。

雖然可以用深比較方式來判斷,但是深比較類似於深拷貝,遞歸操作,性能開銷比較大。

為此,可以對元件盡可能的拆分,讓元件的props和state物件資料達到扁平化,結合使用PureRenderMin或PureComponent來判斷元件是否更新,可以更好地提升react的效能,不需要開發人員過多關心。

元件拆分

元件拆分,在react中就是將元件盡可能的細分,以便於重複使用和最佳化。拆分的具體原則:

盡量使拆分後的元件更容易判斷是否更新

這不太好理解,舉個例子吧:假設我們定義一個父元件,其包含了5000個子組件。有一個輸入框輸入操作,每次輸入一個數字,對應的那個子組件背景色變紅。

react元件拆分的重要性

本例中,輸入框元件和列表子元件有著明顯的不同,一個是動態的,輸入值比較頻繁;一個是相對靜態的,不管input怎麼輸入它就是5000項。輸入框每輸入一個數字都會導致所有元件re-render,這樣就會造成列表子元件不必要的更新。

可以看出,上面列表元件的更新不容易被取消,因為輸入元件和列表子元件的狀態都置於父元件state中,二者共用;react不可能用shouldComponentUpdate的回傳值來使組件一部分組件更新,另一部分不更新。只有把他們拆分為不同的組件,每個組件只關心對應的props。分割的清單元件只關心自己那部分屬性,其他元件導致父元件的更新在清單元件中可以透過判斷自己關心的屬性值狀況來決定是否更新,這樣才能更好地進行元件最佳化。

盡量讓分割元件的props和state資料扁平化

這主要是從元件最佳化的角度考慮的,如果元件不需過度關注效能,可以忽略。

拆分元件之所以扁平化,是因為React提供的最佳化方案PureRenderMin或PureComponent是淺比較元件的props和state來決定是否更新元件。

上面的清單元件中,this.state.items存放的是物件數組,為了更好的判斷每個清單是否需要更新,可以將每個li清單項目單獨拆分為一個清單項目元件,每個列表項相關的props就是items數組中的每個對象,這種扁平化資料很容易判斷是否資料有變化。

元件分割的一個例子

為了這篇文章專門寫了一個關於新增展示Todo清單的事例庫。克隆程式碼到本地可以在本地運行效果。

該事例庫是一個有著5000項的Todo列表,可以刪除並新增Todo項目。此事例展示了組件分割前和分割後的體驗比較情況,可以發現有效能明顯的提升。

下面我們結合react的效能偵測工具react-addons-perf來說明元件拆分的情況。

拆分前的元件TodosBeforeDivision的render部分內容如下:

react元件拆分的重要性

元件拆分前,輸入框輸入字元、增加todo或刪除todo項可以看出有明顯的卡頓現象,如下圖所示:

react元件拆分的重要性

為了弄清楚是什麼原因導致卡頓現象,我們使用chrome的devTool來定位,具體的做法是使用最新版的chrome瀏覽器的Performance選項來完成。先點擊該選項中的record按鈕開始記錄,這時我們在組件輸入框輸入一個字符,然後點擊stop來停止記錄,我們會看到組件從輸入開始到結束這段時間內的一個性能profile。

react元件拆分的重要性

從圖可以看出我們在輸入單一字元時,輸入框的input事件邏輯幾乎佔據整個回應時間,具體的處理邏輯主要是react層面的batchedUpdates方法批次更新清單元件,而不是使用者自訂的邏輯。

那麼,批次更新為啥佔據這麼多時間呢,為了搞清楚原因,我們藉助基於react-addons-perf的chrome插件chrome-react-perf,它以chrome插件的形式輸出分析的結果。

使用該插件需要注意一點的是:

chrome-react-perf插件的使用需要在專案中引入react-addons-perf模組,並必須將其物件掛載到window全域物件的Perf屬性上,否則不能使用。

在devTool工具中選擇Perf選項試圖,點擊start按鈕後其變成stop按鈕,在組件輸入框中輸入一個字符,然後點擊Perf試圖中的stop按鈕,就會得出對應的性能試圖。

react元件拆分的重要性

上圖提供的4個視圖中,Print Wasted對分析效能最有幫組,它表示元件沒有變化但是參與了更新過程,即浪費了re- render和vdom-diff這個過程,是毫無意義的過程。從圖可以看出:TodosBeforeDivision和TodoItem元件分別浪費了167.88ms、144.47ms,完全可以透過分割元件來避免的開銷,這是react效能最佳化重點。

為此我們需要對TodosBeforeDivision元件進行拆分,拆分為一個帶有input和button的動態元件AddTodoForm和一個相對靜態的元件TodoList。二者分別繼承React.PureComponent可以避免不必要的元件更新。

react元件拆分的重要性

其中TodoList元件也需要為每項Todo任務拆分為一個元件TodoItem,這樣每個TodoItem元件的props物件為扁平化的數據,可以充分利用React .PureComponent來進行物件淺比較從而更好地決定元件是否要更新,這樣避免了新增或刪除一個TodoItem項目時,其他TodoItem元件不必更新。

react元件拆分的重要性

這樣拆分後的元件,在用上面的效能偵測工具查看對應的效果:

react元件拆分的重要性

從上面的截圖可以看出,拆分後的組件性能有了上百倍的提升,雖然其中還包含一些其他優化,例如不將function在組件屬性位置綁定this以及常量對象props緩存起來等避免每次re -render時重新產生新的function和新的物件props。

總的來說,對react元件進行拆分對react效能的提升是非常重要的,這也是react效能最佳化的一個方向。

相關推薦:react教學

以上是react元件拆分的重要性的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:cnblogs.com。如有侵權,請聯絡admin@php.cn刪除