高德納: "我們應(yīng)該忘記忽略很小的性能優(yōu)化,可以說(shuō)97%的情況下,過(guò)早的優(yōu)化是萬(wàn)惡之源,而我們應(yīng)該關(guān)心對(duì)性能影響最關(guān)鍵的另外3%的代碼。"
創(chuàng)新互聯(lián)建站2013年開(kāi)創(chuàng)至今,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目網(wǎng)站建設(shè)、成都網(wǎng)站建設(shè)網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元金川做網(wǎng)站,已為上家服務(wù),為金川各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:13518219792
不要將性能優(yōu)化的精力浪費(fèi)在對(duì)整體性能提高不大的代碼上,而對(duì)性能有關(guān)鍵影響的部分,優(yōu)化并不嫌早。因?yàn)椋瑢?duì)性能影響最關(guān)鍵的部分,往往涉及解決方案核心,決定整體的架構(gòu),將來(lái)要改變的時(shí)候牽扯更大。
1. 單個(gè)React組件的性能優(yōu)化
React利用Virtual DOM來(lái)提升渲染性能,雖然每一次頁(yè)面更新都是最組件的從新渲染,但是并不是將之前的渲染內(nèi)容全部拋棄重來(lái),借助Virtual DOM,React能夠計(jì)算出對(duì)DOM樹(shù)的最少修改,這就是React默認(rèn)情況下渲染都很迅速的秘訣;
不過(guò),雖然Virtual DOM能夠?qū)⒚看蜠OM操作量減少到最小,但,計(jì)算和比較Virtual DOM依然是一個(gè)復(fù)雜的過(guò)程;
當(dāng)然,如果能夠在開(kāi)始計(jì)算Virtual DOM之前就判斷渲染的結(jié)果不會(huì)有變化,那么就可以不進(jìn)行Virtual DOM計(jì)算和比較,速度就會(huì)更快。
2.shouldComponentUpdate的默認(rèn)實(shí)現(xiàn)方式
既然可以對(duì)組件在開(kāi)始計(jì)算Virtual DOM之前判斷渲染結(jié)果不會(huì)有變化時(shí),阻止渲染的進(jìn)行,從而提升性能,那么我們自然想到使用shouldComponentUpdate(nextProp,nextState)
shouldComponentUpdate函數(shù)在render函數(shù)之前調(diào)用,決定“什么時(shí)候不需要從新渲染”;
即返回一個(gè)布爾值,決定更新是否進(jìn)行下去,默認(rèn)返回true,若返回false則中斷更新;
shouldComponentUpdate(nextProp,nextState){ return (nextProp.completed !== this.props.completed) || (nextProp.text !== this.props.text) }
其中nextProps為此次更新傳入的props,對(duì)于這個(gè)組件,影響渲染內(nèi)容的prop只有completed和text,只要確保這兩個(gè)prop沒(méi)有變化,shouldComponentUpdate就可以返回false阻止沒(méi)必要的更新
但是,上述的比較只是‘淺層比較',如果類型是基本類型,只要值相同,那么“淺層比較”
也會(huì)認(rèn)為二者相同:
那,如果prop的類型是復(fù)雜的對(duì)象怎么辦?
對(duì)于復(fù)雜對(duì)象,‘淺層比較'的方式只看這兩個(gè)prop是不是同一個(gè)對(duì)象的引用,如果不是,哪怕對(duì)象中的內(nèi)容完全一樣也會(huì)認(rèn)為是不同的兩個(gè)prop。那么使用“深層比較”:但對(duì)對(duì)象的結(jié)構(gòu)是無(wú)法預(yù)知的,如果遞歸對(duì)每個(gè)字段都進(jìn)行“深層比較”,不光會(huì)讓代碼更加復(fù)雜,也可能會(huì)造成性能問(wèn)題。
所以,要想判斷前后的對(duì)象類型的prop是相同的,就必須要保證prop是指向同一個(gè)JavaScript對(duì)象:
<Foo styleProp = {{color: "red"}}>
要避免使用上面的傳入方式,應(yīng)為每次渲染都會(huì)重新創(chuàng)建{color: "red"}對(duì)象,引用地址每次都不同,將導(dǎo)致每次的styleProp都不同。
const footStyle = {color: "red"};//確保這個(gè)初始化只執(zhí)行一次,不要放在render函數(shù)中 <Foo styleProp = {footStyle}>
使用‘單例模式'確保傳入的styleProp指向同一個(gè)對(duì)象
如果是函數(shù)呢?
<Foo onToggle={() => onToggleTodo(item.id)}/>
應(yīng)該避免使用上面的函數(shù)傳遞模式,因?yàn)檫@里賦值的是一個(gè)匿名函數(shù),而且是在賦值的時(shí)候產(chǎn)生的,也就是說(shuō)每次渲染都會(huì)產(chǎn)生一個(gè)新的函數(shù),這就是問(wèn)題所在。
如果要傳遞的prop很多呢?
恩~~用React-Redux的話,有對(duì)shouldComponentUpdate的默認(rèn)實(shí)現(xiàn)。
3. 對(duì)多個(gè)React組件的性能優(yōu)化
當(dāng)一個(gè)React組件被裝載、更新和卸載時(shí),組件的一序列生命周期函數(shù)會(huì)被調(diào)用。不過(guò),這些生命周期函數(shù)是針對(duì)一個(gè)特定的React組件函數(shù),在一個(gè)應(yīng)用中,從上而下有很多React組件組合起來(lái),它們之間的渲染過(guò)程要更加復(fù)雜。
同樣一個(gè)組件的渲染過(guò)程也要考慮三個(gè)過(guò)程:裝載階段、更新階段、卸載階段
對(duì)于裝載階段,組件無(wú)論如何都要徹底渲染一次,從這個(gè)React組件往下的所有子組件,都要經(jīng)歷一遍React組件的裝載生命
周期,所以并沒(méi)有多少優(yōu)化的事情可做。
對(duì)于卸載階段,只有一個(gè)生命周期函數(shù)componentWillUnmount,這個(gè)函數(shù)只是清理componentDidMount添加的事件處理監(jiān)聽(tīng)等收尾工作,所以,也沒(méi)有什么可優(yōu)化的空間;
4. React更新階段的調(diào)和(Reconciliation)過(guò)程
在組件更新過(guò)程,會(huì)構(gòu)建更新Virtual DOM,并將其與之前的Virtual DOM進(jìn)行比較,從而找出不同之處,使用最少的DOM操作進(jìn)行更新
調(diào)和過(guò)程:即React更新中對(duì)Virtual DOM找不同的過(guò)程,通常對(duì)比兩個(gè)N個(gè)節(jié)點(diǎn)的樹(shù)形結(jié)構(gòu)的算法,時(shí)間復(fù)雜度是O(n*3),如果直接
使用默認(rèn)對(duì)比,節(jié)點(diǎn)過(guò)多的話,需要操作的數(shù)量太多,而React不可能采用這種算法;
React實(shí)際采用的算法時(shí)間復(fù)雜度是O(N)(時(shí)間復(fù)雜度只是對(duì)一個(gè)算法最好和最差情況下需要的指令操作數(shù)量級(jí)的估量)
React的Reconciliation算法并不復(fù)雜,首先檢查兩個(gè)樹(shù)形的根節(jié)點(diǎn)的類型是否相同,根據(jù)相同或者不同有不同的處理方式:
節(jié)點(diǎn)類型不同的情況
如果樹(shù)形節(jié)點(diǎn)的類型不相同,那就意味著改動(dòng)很大,直接認(rèn)為原來(lái)的那個(gè)樹(shù)形結(jié)構(gòu)已經(jīng)沒(méi)用,可以扔掉,需要從新構(gòu)建DOM樹(shù),原有的樹(shù)形上的React組件便會(huì)經(jīng)歷“卸載”的生命周期;
也就是說(shuō),對(duì)于Virtual DOM樹(shù)這是一個(gè)“更新”過(guò)程,但是卻可能引發(fā)這個(gè)樹(shù)結(jié)構(gòu)上某些組件的“裝載”和“卸載”過(guò)程
如:
更新前
<div> <Todos /> </div>
我們想要更新成這樣:
<span> <Todos /> </span>
>1. 那么在作比較的時(shí)候,一看根節(jié)點(diǎn)原來(lái)是div,新的是span,類型就不一樣了,那么這個(gè)算法就廢棄之前的div包括里面的所有子節(jié)點(diǎn),從新構(gòu)建一個(gè)span節(jié)點(diǎn)和子節(jié)點(diǎn);
>2. 很明顯因?yàn)楦?jié)點(diǎn)不同就將所有的子節(jié)點(diǎn)從新構(gòu)建,這很浪費(fèi),但是為了避免O(N*3)的時(shí)間復(fù)雜度,React這能選擇這種比較簡(jiǎn)單、快捷的方法;
>3. 所以,作為開(kāi)發(fā)者,我們一定要避免上面的浪費(fèi)的情景出現(xiàn)
節(jié)點(diǎn)類型相同的情況
如果兩個(gè)節(jié)點(diǎn)類型相同時(shí),對(duì)于DOM元素,React會(huì)保留節(jié)點(diǎn)對(duì)應(yīng)的DOM元素,只對(duì)其節(jié)點(diǎn)的屬性和內(nèi)容做對(duì)比,然后只修改更新的部分;
節(jié)點(diǎn)類型相同時(shí),對(duì)于React組件類型,React做得是根據(jù)新節(jié)點(diǎn)的props去更新節(jié)點(diǎn)的組件實(shí)例,引發(fā)組件的更新過(guò)程;
在處理完根節(jié)點(diǎn)對(duì)比后,React的算法會(huì)對(duì)根節(jié)點(diǎn)的每一個(gè)子節(jié)點(diǎn)重復(fù)一樣的操作
多個(gè)相同子組件的情況
如果最初組件狀態(tài)為:
<ul> <TodoItem text = "First" /> <TodoItem text = "Second" /> </ul>
更新后為:
<ul> <TodoItem text = "First" /> <TodoItem text = "Second" /> <TodoItem text = "Third" /> </ul>
那么React會(huì)創(chuàng)建一個(gè)新的TodoItem組件實(shí)例,而前兩個(gè)則進(jìn)行正常的更新過(guò)程但是,如果更新后為:
<ul> <TodoItem text = "Zero" /> <TodoItem text = "First" /> <TodoItem text = "Second" /> </ul>
(這將暴露一個(gè)問(wèn)題)理想處理方式是,創(chuàng)建一個(gè)新的TodoItem組件實(shí)例放在第一位,后兩個(gè)進(jìn)入自然更新過(guò)程
但是要讓react按照這種方式,就必須找兩個(gè)子組件的不同之處,而現(xiàn)有計(jì)算兩個(gè)序列差異的算法時(shí)間是O(N*2),顯然則
不適合對(duì)性能要求很高的場(chǎng)景,所以React選擇了一個(gè)看起來(lái)很傻的辦法,即挨個(gè)比較每個(gè)子組件;
React首先認(rèn)為把text為First的組件的text改為Zero,Second的改為First,最后創(chuàng)建一個(gè)text為Second的組件,這樣便會(huì)破原有的兩個(gè)組件完成一個(gè)更新過(guò)程,并創(chuàng)建一個(gè)text為Second的新組件
這顯然是一個(gè)浪費(fèi),React也意到,并提供了方克服,不過(guò)需要開(kāi)發(fā)人員提供一點(diǎn)幫助,這就是key
Key的使用
key屬性可以明確的告訴React每個(gè)組件的唯一標(biāo)識(shí)
如果最初組件狀態(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>
因?yàn)橛形ㄒ粯?biāo)識(shí)key,React可以根據(jù)key值,知道現(xiàn)在的第二和第三個(gè)組件就是之前的第一和第二個(gè),便用原來(lái)的props啟動(dòng)更新過(guò)程,這樣shouldComponentUpdate就會(huì)發(fā)生作用,避免無(wú)謂的更新;
注意:因?yàn)樽鳛榻M件的唯一標(biāo)識(shí),所以key必須唯一,且不可變
下面的代碼是錯(cuò)誤的例子:
<ul> todos.map((item,index) => { <TodoItem key={index} text={item.text} /> }) </ul>
使用數(shù)組下標(biāo)作為key值,看起來(lái)唯一,但不穩(wěn)定,因?yàn)殡S著todos數(shù)組值的不同,同樣一個(gè)組件實(shí)例在不同的更新過(guò)程中數(shù)組的下標(biāo)完全可能不同,把下標(biāo)當(dāng)做可以就會(huì)讓React亂套,記住key不僅要唯一還要確保穩(wěn)定不可變
需要注意:雖然key是一個(gè)prop,但是接受key的組件不能讀取key的值,因?yàn)閗ey和ref是React保留的兩個(gè)特殊prop,并沒(méi)有預(yù)期讓組將直接訪問(wèn)。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。
當(dāng)前標(biāo)題:淺談React組件之性能優(yōu)化
轉(zhuǎn)載來(lái)源:http://vcdvsql.cn/article20/gjiojo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供服務(wù)器托管、全網(wǎng)營(yíng)銷推廣、網(wǎng)站內(nèi)鏈、微信公眾號(hào)、定制網(wǎng)站、域名注冊(cè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)