本來想深入說一下virtual dom 與diff,但是最近很忙,就簡單聊聊react的diff算法吧。
diff算法
傳統(tǒng)的diff算法是通過循環(huán)遞歸對節(jié)點進行一次對比稀余,從而計算一棵樹型結(jié)構(gòu)轉(zhuǎn)換成另一顆屬性結(jié)構(gòu)。這種方法效率低下趋翻,如果采用這種方式進行渲染性能的消耗會異常大这溅。那么react是怎么解決這個的呢住闯?
1.dom節(jié)點跨層級的移動操作特別少咐柜,少到可以忽略不計黔龟。
react對樹的算法進行了簡明的優(yōu)化,是的dom樹通過分層比較dom的差異讨惩,并且只會對比同一層辟癌,忽略跨層級的差異
例如,對比一個父節(jié)點下所有子節(jié)點荐捻,當發(fā)現(xiàn)有一個子節(jié)點不在時黍少,即刪除該子節(jié)點下所有內(nèi)容,這樣減少了很多不必要的遍歷处面。
假如用戶出現(xiàn)了跨層級移動操作
假如厂置,在dom下有兩個節(jié)點dom1、dom2魂角。此時我們需要將dom1移植到dom2下昵济。
react發(fā)現(xiàn)dom1不見,它會不管三七二十一野揪,將整個dom1刪除砸紊,根本不會考慮dom1下子節(jié)點的問題。
但是囱挑,現(xiàn)在我們是將dom1移到dom2下了。react會執(zhí)行以下操作沼溜。
在原有dom2節(jié)點平挑,新建dom1,然后在依次新建dom1的子節(jié)點系草。
最后刪除原來dom1節(jié)點通熄。
沒錯,就是新建然后刪除操作找都。這就是react針對特殊情況的特殊對待唇辨。
但是針對這種特殊情況,會大大影響性能能耻,所以應該避免dom節(jié)點跨層操作的為標題
2.不同類的組件很少存在相似DOM的情況赏枚,只會比較同類組件
針對組件亡驰,擁有同一類的兩個組件將按照原策略進行比較,擁有不同類的兩個組件將會生成不同的樹結(jié)構(gòu)饿幅,當react判斷組件為dirty component凡辱,從而替換整個組件下所有子節(jié)點。
用戶可以通過shouldComponentUpdate()來判斷是否需要更新組件栗恩。
如果透乾,在開發(fā)中我們需要將A組件換成B組件,A與B不是同類組件時磕秤,react會毫不猶豫的刪除A乳乌,新建B及其子節(jié)點。不管A與B是否有相同的結(jié)構(gòu)市咆。
但是假如在不同類汉操,但是結(jié)構(gòu)相似的dom,將會大大影響react性能床绪。
react官方也說過客情,在開發(fā)過程中不同類型很少存在相似dom樹。
3.當節(jié)點處于同一層級時候癞己。
(1).新組建類型不存在舊的集合里膀斋,對新節(jié)點執(zhí)行插入makeInsertMarkup()
(2).舊的集合里有新的組件類型,引動操作痹雅。makeMove()
(3).舊的組件類型在新的集合里就有仰担,但是不能直接服用,或者則執(zhí)行makeRemove()操作绩社。
react通過對同一級別的同組節(jié)點添加唯一的key值摔蓝,進行區(qū)分。
react首先會對新集合循環(huán)遍歷愉耙,通過位移的key值判斷新舊集合中是否存在相同的節(jié)點贮尉,如果存在,則進行移動操作朴沿,否則不執(zhí)行操作猜谚。
執(zhí)行移動操作:
當前節(jié)點在舊集合位置mountIndex與舊集合中l(wèi)astIndex(表示訪問過的節(jié)點在舊集合中最右側(cè)的位置,會一直變化)進行比較赌渣。
如果新節(jié)點中訪問的節(jié)點比lastIndex大魏铅,證明訪問的節(jié)點比舊集合最后一個節(jié)點還靠右,因此該節(jié)點不會影響其他節(jié)點坚芜,不需要添加到差異隊列中览芳。只用小于lastIndex時,才會進行操作鸿竖。
通過上文我們可以了解react的diff將傳統(tǒng)的循環(huán)迭代比較沧竟,通過自己特有的規(guī)則铸敏,優(yōu)化了很多。規(guī)則1就是屯仗,react忽略dom節(jié)點跨層級的移動操作搞坝。其次,react只會比較同類組件魁袜。要是萬一出現(xiàn)特殊情況呢桩撮,react針對特殊情況將實現(xiàn)新增刪除操作。但是峰弹,這樣會對react本身性能有很大影響店量。
所以在開發(fā)中我們也要避免,盡量不做跨層操作鞠呈,在設(shè)計組件開發(fā)的時候融师,相似dom樹,可以想辦法設(shè)計相同的類蚁吝。當然旱爆,更要避免新增刪除dom,而是用屬性控制窘茁。最簡單的例子:
將下面代碼
if(...){
dom = (<div>
....
</div>)
}else{
dom =null;
}
用如下代替
if(...){
attrs = {display:'block'}
}else{
attrs = {display:'none'}
}
<div style={attr}>...</div>
這個例子很簡單怀伦,因為作者見到過這樣用的,(曾經(jīng)面試過這樣一個人山林,自稱熟悉房待,做過很多項目,但是...不過確實第一種很節(jié)省時間)就在這里做個反面教材驼抹。
其實在做更大的項目桑孩,開發(fā)更復雜的組件的時候,我們會針對react這些特性設(shè)計出性能更加強大的組件框冀,這才是由小白與大神真正的區(qū)別 流椒, 額--之一。這就是有可能你做了很多項目但是你依然是個小白明也,而有些人做的項目并不是很多卻已經(jīng)成為大神了镣隶。
React基礎(chǔ)1--生命周期詳解
React基礎(chǔ)2--深入挖掘setState
如果有些錯的地方歡迎留言。不過作者肯定懶得改诡右。