溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

react組件拆分的示例分析

發(fā)布時(shí)間:2021-02-20 13:59:47 來源:億速云 閱讀:152 作者:小新 欄目:web開發(fā)

這篇文章將為大家詳細(xì)講解有關(guān)react組件拆分的示例分析,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。

前言:

React 是一個(gè)用于構(gòu)建用戶界面的 JAVASCRIPT 庫。主要用于構(gòu)建UI,很多人認(rèn)為 React 是 MVC 中的 V(視圖)。

react采用虛擬DOM技術(shù)減少Javascript與真正DOM的交互,提升了前端性能;采用單向數(shù)據(jù)流機(jī)制,父組件通過props將數(shù)據(jù)傳遞給子組件,這樣讓數(shù)據(jù)流向一目了然。

一旦組件的props或則state發(fā)生改變,組件及其子組件都將重新re-render和vdom-diff,從而完成數(shù)據(jù)的流向交互。但是這種機(jī)制在某些情況下比如說數(shù)據(jù)量較大的情況下可能會存在一些性能問題。下面就來分析react的性能瓶頸,并用結(jié)合著react-addons-perf工具來說明react組件拆分的重要性。

react性能瓶頸

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

初始組件化
該階段會執(zhí)行組件及其所有子組件的render方法,從而生成第一版的虛擬dom。

組件更新渲染。
組件的props或者state任意發(fā)生改變就會觸發(fā)組件的更新渲染。默認(rèn)情況下其也會執(zhí)行該組件及其所有子組件的render方法獲取新的虛擬dom。

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

react組件更新流程

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

執(zhí)行該組件及其所有子組件的render方法獲取更新后的虛擬DOM,即re-render,即使子組件無需更新。

然后對新舊兩份虛擬DOM進(jìn)行diff來進(jìn)行組件的更新

在這個(gè)過程中,可以通過組件的shouldComponentUpdate方法返回值來決定是否需要re-render。

react的整個(gè)更新渲染流程可以借用一張圖來加以說明:

react組件拆分的示例分析

默認(rèn)地,組件的shouldComponentUpdate返回true,即React默認(rèn)會調(diào)用所有組件的render方法來生成新的虛擬DOM, 然后跟舊的虛擬DOM比較來決定組件最終是否需要更新。

react性能瓶頸

借圖說話,例如下圖是一個(gè)組件結(jié)構(gòu)tree,當(dāng)我們要更新某個(gè)子組件的時(shí)候,如下圖的綠色組件(從根組件傳遞下來應(yīng)用在綠色組件上的數(shù)據(jù)發(fā)生改變):

react組件拆分的示例分析

理想情況下,我們只希望關(guān)鍵路徑上的組件進(jìn)行更新,如下圖:

react組件拆分的示例分析

但是,實(shí)際效果卻是每個(gè)組件都完成re-render和virtual-DOM diff過程,雖然組件沒有變更,這明顯是一種浪費(fèi)。如下圖黃色部分表示浪費(fèi)的re-render和virtual-DOM diff。

react組件拆分的示例分析

根據(jù)上面的分析,react的性能瓶頸主要表現(xiàn)在:

對于props和state沒有變化的組件,react也要重新生成虛擬DOM及虛擬DOM的diff。

用shouldComponentUpdate來進(jìn)行性能優(yōu)化

針對react的性能瓶頸,我們可以通過react提供的shouldComponentUpdate方法來做點(diǎn)優(yōu)化的事,可以有選擇的進(jìn)行組件更新,從而提升react的性能,具體如下:

shouldComponentUpdate需要判斷當(dāng)前屬性和狀態(tài)是否和上一次的相同,如果相同則不需要執(zhí)行后續(xù)生成虛擬DOM及其diff的過程,否則需要更新。

具體可以這么顯示實(shí)現(xiàn):

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

其中,isEqual方法為判斷兩個(gè)對象是否相等(指的是其對象內(nèi)容相等,而不是全等)。

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

方案對組件的shouldComponentUpdate進(jìn)行了封裝處理,實(shí)現(xiàn)對組件的當(dāng)前屬性和狀態(tài)與上一次的進(jìn)行淺對比,從而決定組件是否需要更新。

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

PureRenderMin
一種是基于ES5的React.createClass創(chuàng)建的組件,配合該形式下的mixins方式來組合PureRenderMixin提供的shouldComponentUpdate方法。當(dāng)然用ES6創(chuàng)建的組件也能使用該方案。

react組件拆分的示例分析

PureComponent
該方案是在React 15.3.0版本發(fā)布的針對ES6而增加的一個(gè)組件基類:React.PureComponent。這明顯對ES6方式創(chuàng)建的組件更加友好。

react組件拆分的示例分析

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

react組件拆分的示例分析

因?yàn)閟tate的value被賦予另一個(gè)對象,使nextState.value與this.props.value始終不等,導(dǎo)致淺比較通過不了。在實(shí)際項(xiàng)目中,這種嵌套的對象結(jié)果是很常見的,如果使用PureRenderMin或者PureComponent方式時(shí)起不到應(yīng)有的效果。

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

為此,可以對組件盡可能的拆分,使組件的props和state對象數(shù)據(jù)達(dá)到扁平化,結(jié)合著使用PureRenderMin或者PureComponent來判斷組件是否更新,可以更好地提升react的性能,不需要開發(fā)人員過多關(guān)心。

組件拆分

組件拆分,在react中就是將組件盡可能的細(xì)分,便于復(fù)用和優(yōu)化。拆分的具體原則:

盡量使拆分后的組件更容易判斷是否更新

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

react組件拆分的示例分析

本例中,輸入框組件和列表子組件有著明顯的不同,一個(gè)是動(dòng)態(tài)的,輸入值比較頻繁;一個(gè)是相對靜態(tài)的,不管input怎么輸入它就是5000項(xiàng)。輸入框每輸入一個(gè)數(shù)字都會導(dǎo)致所有組件re-render,這樣就會造成列表子組件不必要的更新。

可以看出,上面列表組件的更新不容易被取消,因?yàn)檩斎虢M件和列表子組件的狀態(tài)都置于父組件state中,二者共享;react不可能用shouldComponentUpdate的返回值來使組件一部分組件更新,另一部分不更新。 只有把他們拆分為不同的組件,每個(gè)組件只關(guān)心對應(yīng)的props。拆分的列表組件只關(guān)心自己那部分屬性,其他組件導(dǎo)致父組件的更新在列表組件中可以通過判斷自己關(guān)心的屬性值情況來決定是否更新,這樣才能更好地進(jìn)行組件優(yōu)化。

盡量使拆分組件的props和state數(shù)據(jù)扁平化

這主要是從組件優(yōu)化的角度考慮的,如果組件不需過多關(guān)注性能,可以忽略。

拆分組件之所以扁平化,是因?yàn)镽eact提供的優(yōu)化方案PureRenderMin或者PureComponent是淺比較組件的props和state來決定是否更新組件。

上面的列表組件中,this.state.items存放的是對象數(shù)組,為了更好的判斷每項(xiàng)列表是否需要更新,可以將每個(gè)li列表項(xiàng)單獨(dú)拆分為一個(gè)列表項(xiàng)組件,每個(gè)列表項(xiàng)相關(guān)的props就是items數(shù)組中的每個(gè)對象,這種扁平化數(shù)據(jù)很容易判斷是否數(shù)據(jù)發(fā)生變化。

組件拆分的一個(gè)例子

為了這篇文章專門寫了一個(gè)有關(guān)添加展示Todo列表的事例庫??寺〈a到本地可以在本地運(yùn)行效果。

該事例庫是一個(gè)有著5000項(xiàng)的Todo列表,可以刪除和新增Todo項(xiàng)。該事例展示了組件拆分前和拆分后的體驗(yàn)對比情況,可以發(fā)現(xiàn)有性能明顯的提升。

下面我們結(jié)合react的性能檢測工具react-addons-perf來說明組件拆分的情況。

拆分前的組件TodosBeforeDivision的render部分內(nèi)容如下:

react組件拆分的示例分析

組件拆分前,輸入框輸入字符、增加todo或者刪除todo項(xiàng)可以看出有明顯的卡頓現(xiàn)象,如下圖所示:

react組件拆分的示例分析

為了弄清楚是什么原因?qū)е驴D現(xiàn)象,我們使用chrome的devTool來定位,具體的做法是使用最新版的chrome瀏覽器的Performance選項(xiàng)來完成。先點(diǎn)擊該選項(xiàng)中的record按鈕開始記錄,這時(shí)我們在組件輸入框輸入一個(gè)字符,然后點(diǎn)擊stop來停止記錄,我們會看到組件從輸入開始到結(jié)束這段時(shí)間內(nèi)的一個(gè)性能profile。

react組件拆分的示例分析

從圖可以看出我們在輸入單個(gè)字符時(shí),輸入框的input事件邏輯幾乎占據(jù)整個(gè)響應(yīng)時(shí)間,具體的處理邏輯主要是react層面的batchedUpdates方法批量更新列表組件,而不是用戶自定義的邏輯。

那么,批量更新為啥占據(jù)這么多時(shí)間呢,為了搞清楚原因,我們借助基于react-addons-perf的chrome插件chrome-react-perf,它以chrome插件的形式輸出分析的結(jié)果。

使用該插件需要注意一點(diǎn)的是:

chrome-react-perf插件的使用需要在項(xiàng)目中引入react-addons-perf模塊,并必須將其對象掛載到window全局對象的Perf屬性上,否則不能使用。

在devTool工具中選擇Perf選項(xiàng)試圖,點(diǎn)擊start按鈕后其變成stop按鈕,在組件輸入框中輸入一個(gè)字符,然后點(diǎn)擊Perf試圖中的stop按鈕,就會得出對應(yīng)的性能試圖。

react組件拆分的示例分析

上圖提供的4個(gè)視圖中,Print Wasted對分析性能最有幫組,它表示組件沒有變化但是參與了更新過程,即浪費(fèi)了re-render和vdom-diff這一過程,是毫無意義的過程。從圖可以看出:TodosBeforeDivision和TodoItem組件分別浪費(fèi)了167.88ms、144.47ms,這完全可以通過拆分組件避免的開銷,這是react性能優(yōu)化重點(diǎn)。

為此我們需要對TodosBeforeDivision組件進(jìn)行拆分,拆分為一個(gè)帶有input和button的動(dòng)態(tài)組件AddTodoForm和一個(gè)相對靜態(tài)的組件TodoList。二者分別繼承React.PureComponent可以避免不必要的組件更新。

react組件拆分的示例分析

其中TodoList組件還需要為每項(xiàng)Todo任務(wù)拆分為一個(gè)組件TodoItem,這樣每個(gè)TodoItem組件的props對象為扁平化的數(shù)據(jù),可以充分利用React.PureComponent來進(jìn)行對象淺比較從而更好地決定組件是否要更新,這樣避免了新增或者刪除一個(gè)TodoItem項(xiàng)時(shí),其他TodoItem組件不必更新。

react組件拆分的示例分析

這樣拆分后的組件,在用上面的性能檢測工具查看對應(yīng)的效果:

react組件拆分的示例分析

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

總的來說,對react組件進(jìn)行拆分對react性能的提升是非常重要的,這也是react性能優(yōu)化的一個(gè)方向。

關(guān)于“react組件拆分的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識,如果覺得文章不錯(cuò),請把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI