国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > JS > 正文

淺談React組件之性能優(yōu)化

2024-05-06 16:42:47
字體:
供稿:網(wǎng)友

高德納: "我們應(yīng)該忘記忽略很小的性能優(yōu)化,可以說97%的情況下,過早的優(yōu)化是萬惡之源,而我們應(yīng)該關(guān)心對性能影響最關(guān)鍵的另外3%的代碼。"

不要將性能優(yōu)化的精力浪費在對整體性能提高不大的代碼上,而對性能有關(guān)鍵影響的部分,優(yōu)化并不嫌早。因為,對性能影響最關(guān)鍵的部分,往往涉及解決方案核心,決定整體的架構(gòu),將來要改變的時候牽扯更大。

1. 單個React組件的性能優(yōu)化

React利用Virtual DOM來提升渲染性能,雖然每一次頁面更新都是最組件的從新渲染,但是并不是將之前的渲染內(nèi)容全部拋棄重來,借助Virtual DOM,React能夠計算出對DOM樹的最少修改,這就是React默認(rèn)情況下渲染都很迅速的秘訣;

不過,雖然Virtual DOM能夠?qū)⒚看蜠OM操作量減少到最小,但,計算和比較Virtual DOM依然是一個復(fù)雜的過程;

當(dāng)然,如果能夠在開始計算Virtual DOM之前就判斷渲染的結(jié)果不會有變化,那么就可以不進(jìn)行Virtual DOM計算和比較,速度就會更快。

2.shouldComponentUpdate的默認(rèn)實現(xiàn)方式

既然可以對組件在開始計算Virtual DOM之前判斷渲染結(jié)果不會有變化時,阻止渲染的進(jìn)行,從而提升性能,那么我們自然想到使用shouldComponentUpdate(nextProp,nextState)

shouldComponentUpdate函數(shù)在render函數(shù)之前調(diào)用,決定“什么時候不需要從新渲染”;

即返回一個布爾值,決定更新是否進(jìn)行下去,默認(rèn)返回true,若返回false則中斷更新;

shouldComponentUpdate(nextProp,nextState){  return (nextProp.completed !== this.props.completed) ||    (nextProp.text !== this.props.text)}

其中nextProps為此次更新傳入的props,對于這個組件,影響渲染內(nèi)容的prop只有completed和text,只要確保這兩個prop沒有變化,shouldComponentUpdate就可以返回false阻止沒必要的更新

但是,上述的比較只是‘淺層比較',如果類型是基本類型,只要值相同,那么“淺層比較”

也會認(rèn)為二者相同:

那,如果prop的類型是復(fù)雜的對象怎么辦?

對于復(fù)雜對象,‘淺層比較'的方式只看這兩個prop是不是同一個對象的引用,如果不是,哪怕對象中的內(nèi)容完全一樣也會認(rèn)為是不同的兩個prop。那么使用“深層比較”:但對對象的結(jié)構(gòu)是無法預(yù)知的,如果遞歸對每個字段都進(jìn)行“深層比較”,不光會讓代碼更加復(fù)雜,也可能會造成性能問題。

所以,要想判斷前后的對象類型的prop是相同的,就必須要保證prop是指向同一個JavaScript對象:

<Foo styleProp = {{color: "red"}}>

要避免使用上面的傳入方式,應(yīng)為每次渲染都會重新創(chuàng)建{color: "red"}對象,引用地址每次都不同,將導(dǎo)致每次的styleProp都不同。

const footStyle = {color: "red"};//確保這個初始化只執(zhí)行一次,不要放在render函數(shù)中<Foo styleProp = {footStyle}>

使用‘單例模式'確保傳入的styleProp指向同一個對象

如果是函數(shù)呢?

<Foo onToggle={() => onToggleTodo(item.id)}/>

應(yīng)該避免使用上面的函數(shù)傳遞模式,因為這里賦值的是一個匿名函數(shù),而且是在賦值的時候產(chǎn)生的,也就是說每次渲染都會產(chǎn)生一個新的函數(shù),這就是問題所在。

如果要傳遞的prop很多呢?

恩~~用React-Redux的話,有對shouldComponentUpdate的默認(rèn)實現(xiàn)。

3. 對多個React組件的性能優(yōu)化

當(dāng)一個React組件被裝載、更新和卸載時,組件的一序列生命周期函數(shù)會被調(diào)用。不過,這些生命周期函數(shù)是針對一個特定的React組件函數(shù),在一個應(yīng)用中,從上而下有很多React組件組合起來,它們之間的渲染過程要更加復(fù)雜。

同樣一個組件的渲染過程也要考慮三個過程:裝載階段、更新階段、卸載階段

對于裝載階段,組件無論如何都要徹底渲染一次,從這個React組件往下的所有子組件,都要經(jīng)歷一遍React組件的裝載生命
周期,所以并沒有多少優(yōu)化的事情可做。

對于卸載階段,只有一個生命周期函數(shù)componentWillUnmount,這個函數(shù)只是清理componentDidMount添加的事件處理監(jiān)聽等收尾工作,所以,也沒有什么可優(yōu)化的空間;

4. React更新階段的調(diào)和(Reconciliation)過程

在組件更新過程,會構(gòu)建更新Virtual DOM,并將其與之前的Virtual DOM進(jìn)行比較,從而找出不同之處,使用最少的DOM操作進(jìn)行更新

調(diào)和過程:即React更新中對Virtual DOM找不同的過程,通常對比兩個N個節(jié)點的樹形結(jié)構(gòu)的算法,時間復(fù)雜度是O(n*3),如果直接

使用默認(rèn)對比,節(jié)點過多的話,需要操作的數(shù)量太多,而React不可能采用這種算法;

React實際采用的算法時間復(fù)雜度是O(N)(時間復(fù)雜度只是對一個算法最好和最差情況下需要的指令操作數(shù)量級的估量)

React的Reconciliation算法并不復(fù)雜,首先檢查兩個樹形的根節(jié)點的類型是否相同,根據(jù)相同或者不同有不同的處理方式:

節(jié)點類型不同的情況

如果樹形節(jié)點的類型不相同,那就意味著改動很大,直接認(rèn)為原來的那個樹形結(jié)構(gòu)已經(jīng)沒用,可以扔掉,需要從新構(gòu)建DOM樹,原有的樹形上的React組件便會經(jīng)歷“卸載”的生命周期;

也就是說,對于Virtual DOM樹這是一個“更新”過程,但是卻可能引發(fā)這個樹結(jié)構(gòu)上某些組件的“裝載”和“卸載”過程
如:

更新前

 <div>  <Todos /> </div>

我們想要更新成這樣:

 <span>   <Todos /> </span>

>1. 那么在作比較的時候,一看根節(jié)點原來是div,新的是span,類型就不一樣了,那么這個算法就廢棄之前的div包括里面的所有子節(jié)點,從新構(gòu)建一個span節(jié)點和子節(jié)點;

>2. 很明顯因為根節(jié)點不同就將所有的子節(jié)點從新構(gòu)建,這很浪費,但是為了避免O(N*3)的時間復(fù)雜度,React這能選擇這種比較簡單、快捷的方法;

>3. 所以,作為開發(fā)者,我們一定要避免上面的浪費的情景出現(xiàn)

節(jié)點類型相同的情況

如果兩個節(jié)點類型相同時,對于DOM元素,React會保留節(jié)點對應(yīng)的DOM元素,只對其節(jié)點的屬性和內(nèi)容做對比,然后只修改更新的部分;

節(jié)點類型相同時,對于React組件類型,React做得是根據(jù)新節(jié)點的props去更新節(jié)點的組件實例,引發(fā)組件的更新過程;

在處理完根節(jié)點對比后,React的算法會對根節(jié)點的每一個子節(jié)點重復(fù)一樣的操作

多個相同子組件的情況

如果最初組件狀態(tài)為:

<ul>  <TodoItem text = "First" />  <TodoItem text = "Second" /></ul>

更新后為:

<ul>  <TodoItem text = "First" />  <TodoItem text = "Second" />  <TodoItem text = "Third" /></ul>

那么React會創(chuàng)建一個新的TodoItem組件實例,而前兩個則進(jìn)行正常的更新過程但是,如果更新后為:

<ul>  <TodoItem text = "Zero" />  <TodoItem text = "First" />  <TodoItem text = "Second" /></ul>

(這將暴露一個問題)理想處理方式是,創(chuàng)建一個新的TodoItem組件實例放在第一位,后兩個進(jìn)入自然更新過程
但是要讓react按照這種方式,就必須找兩個子組件的不同之處,而現(xiàn)有計算兩個序列差異的算法時間是O(N*2),顯然則
不適合對性能要求很高的場景,所以React選擇了一個看起來很傻的辦法,即挨個比較每個子組件;

React首先認(rèn)為把text為First的組件的text改為Zero,Second的改為First,最后創(chuàng)建一個text為Second的組件,這樣便會破原有的兩個組件完成一個更新過程,并創(chuàng)建一個text為Second的新組件

這顯然是一個浪費,React也意到,并提供了方克服,不過需要開發(fā)人員提供一點幫助,這就是key

Key的使用

key屬性可以明確的告訴React每個組件的唯一標(biāo)識

如果最初組件狀態(tài)為:

<ul>  <TodoItem key={1} text = "First" />  <TodoItem key={2} text = "Second" /></ul>

更新后為:

<ul>  <TodoItem key={0} text = "Zero" />  <TodoItem key={1} text = "First" />  <TodoItem key={2} text = "Second" /></ul>

因為有唯一標(biāo)識key,React可以根據(jù)key值,知道現(xiàn)在的第二和第三個組件就是之前的第一和第二個,便用原來的props啟動更新過程,這樣shouldComponentUpdate就會發(fā)生作用,避免無謂的更新;

注意:因為作為組件的唯一標(biāo)識,所以key必須唯一,且不可變

下面的代碼是錯誤的例子:

<ul>  todos.map((item,index) => {      <TodoItem        key={index}        text={item.text}      />    })</ul>

使用數(shù)組下標(biāo)作為key值,看起來唯一,但不穩(wěn)定,因為隨著todos數(shù)組值的不同,同樣一個組件實例在不同的更新過程中數(shù)組的下標(biāo)完全可能不同,把下標(biāo)當(dāng)做可以就會讓React亂套,記住key不僅要唯一還要確保穩(wěn)定不可變

需要注意:雖然key是一個prop,但是接受key的組件不能讀取key的值,因為key和ref是React保留的兩個特殊prop,并沒有預(yù)期讓組將直接訪問。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持VeVb武林網(wǎng)。


注:相關(guān)教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 时尚| 香格里拉县| 双桥区| 龙海市| 化隆| 武山县| 永昌县| 西宁市| 内丘县| 横峰县| 台山市| 十堰市| 沅陵县| 泉州市| 突泉县| 时尚| 连云港市| 时尚| 锡林郭勒盟| 潞城市| 山西省| 新化县| 泾川县| 六安市| 元朗区| 高尔夫| 波密县| 莫力| 海淀区| 南雄市| 六安市| 金山区| 吴旗县| 申扎县| 扎鲁特旗| 灵川县| 和平县| 左权县| 锡林郭勒盟| 常宁市| 神农架林区|