react是一個(gè)高效的前端框架凰荚,它的高效體現(xiàn)在對(duì)于DOM元素的修改上面整吆,對(duì)比直接操作DOM的原生或者jquery方法牢裳,它的優(yōu)秀之處在于對(duì)DOM元素的查找上面着饥。
想象一下,如果讓你去查找老顏我在我們這個(gè)姓氏宗譜(祈求祖先們的原諒)從唐代到2018年我所在的位置龄广,你會(huì)怎么查硫眯,指不準(zhǔn)你會(huì)從我這個(gè)位置,去看我爸爸的位置择同,然后從我爸爸的位置再去看我爺爺?shù)奈恢昧饺搿!G貌拧裹纳!?/p>
但是,如果你只是知道有我這個(gè)人归斤,卻不知道我在我們家偉大的族譜中哪個(gè)位置痊夭,并且有個(gè)大佬告訴你我們這個(gè)姓氏在唐代的那個(gè)祖先,那想一想脏里,你指不準(zhǔn)會(huì)從我的祖先開始一個(gè)一個(gè)的遍歷他的所有后代她我,想象一下,粗略算一下,假設(shè)從唐代開始番舆,一共有n個(gè)人存在族譜樹上酝碳,那么你大概需要O(n^3)的時(shí)間復(fù)雜度(你肯定沒看過算法,所以你就忽略O(shè)()恨狈,看n^3就夠了)疏哗,假設(shè)這個(gè) n=1000,再假設(shè)你每看一個(gè)人禾怠,你就能記住他的位置返奉,而且你只需要1秒鐘,那么你就需要1000*1000*1000=1000000000吗氏,10億秒芽偏,(?'-')?加油,有興趣你可以算算要多長(zhǎng)時(shí)間:)弦讽,可以說這就是我們實(shí)際用原生js方法去找一個(gè)元素的過程污尉!
這太tnd可怕了,所以react提出了虛擬DOM這個(gè)東西往产!
好了我們來看看被碗,diff
react diff算法
1. diff策略
下面介紹一下react diff算法的3個(gè)策略
Web UI 中DOM節(jié)點(diǎn)跨層級(jí)的移動(dòng)操作特別少,可以忽略不計(jì)
擁有相同類的兩個(gè)組件將會(huì)生成相似的樹形結(jié)構(gòu)仿村,擁有不同類的兩個(gè)組件將會(huì)生成不同的樹形結(jié)構(gòu)锐朴。
對(duì)于同一層級(jí)的一組子節(jié)點(diǎn),它們可以通過唯一id進(jìn)行區(qū)分奠宜。
對(duì)于以上三個(gè)策略包颁,react分別對(duì)tree diff,component diff,element diff進(jìn)行算法優(yōu)化。
2.tree diff
基于策略一压真,WebUI中DOM節(jié)點(diǎn)跨層級(jí)的移動(dòng)操作少的可以忽略不計(jì),React對(duì)Virtual DOM樹進(jìn)行層級(jí)控制蘑险,只會(huì)對(duì)相同層級(jí)的DOM節(jié)點(diǎn)進(jìn)行比較滴肿,即同一個(gè)父元素下的所有子節(jié)點(diǎn),當(dāng)發(fā)現(xiàn)節(jié)點(diǎn)已經(jīng)不存在了佃迄,則會(huì)刪除掉該節(jié)點(diǎn)下所有的子節(jié)點(diǎn)泼差,不會(huì)再進(jìn)行比較。這樣只需要對(duì)DOM樹進(jìn)行一次遍歷呵俏,就可以完成整個(gè)樹的比較堆缘。復(fù)雜度變?yōu)镺(n);
疑問:當(dāng)我們的DOM節(jié)點(diǎn)進(jìn)行跨層級(jí)操作時(shí),diff會(huì)有怎么樣的表現(xiàn)呢普碎?
如下圖所示吼肥,A節(jié)點(diǎn)及其子節(jié)點(diǎn)被整個(gè)移動(dòng)到D節(jié)點(diǎn)下面去,由于React只會(huì)簡(jiǎn)單的考慮同級(jí)節(jié)點(diǎn)的位置變換,而對(duì)于不同層級(jí)的節(jié)點(diǎn)缀皱,只有創(chuàng)建和刪除操作斗这,所以當(dāng)根節(jié)點(diǎn)發(fā)現(xiàn)A節(jié)點(diǎn)消失了,就會(huì)刪除A節(jié)點(diǎn)及其子節(jié)點(diǎn)啤斗,當(dāng)D發(fā)現(xiàn)多了一個(gè)子節(jié)點(diǎn)A表箭,就會(huì)創(chuàng)建新的A作為其子節(jié)點(diǎn)。
此時(shí)钮莲,diff的執(zhí)行情況是:
createA-->createB-->createC-->deleteA
由此可以發(fā)現(xiàn)免钻,當(dāng)出現(xiàn)節(jié)點(diǎn)跨層級(jí)移動(dòng)時(shí),并不會(huì)出現(xiàn)想象中的移動(dòng)操作崔拥,而是會(huì)進(jìn)行刪除伯襟,重新創(chuàng)建的動(dòng)作,這是一種很影響React性能的操作握童。因此官方也不建議進(jìn)行DOM節(jié)點(diǎn)跨層級(jí)的操作姆怪。
3.componnet diff
React是基于組件構(gòu)建應(yīng)用的,對(duì)于組件間的比較所采用的策略也是非常簡(jiǎn)潔和高效的澡绩。
如果是同一個(gè)類型的組件稽揭,則按照原策略進(jìn)行Virtual DOM比較。
如果不是同一類型的組件肥卡,則將其判斷為dirty component溪掀,從而替換整個(gè)組價(jià)下的所有子節(jié)點(diǎn)。
如果是同一個(gè)類型的組件步鉴,有可能經(jīng)過一輪Virtual DOM比較下來揪胃,并沒有發(fā)生變化。如果我們能夠提前確切知道這一點(diǎn)氛琢,那么就可以省下大量的diff運(yùn)算時(shí)間喊递。因此,React允許用戶通過shouldComponentUpdate()來判斷該組件是否需要進(jìn)行diff算法分析阳似。
如下圖所示骚勘,當(dāng)組件D變?yōu)榻M件G時(shí),即使這兩個(gè)組件結(jié)構(gòu)相似撮奏,一旦React判斷D和G是不用類型的組件俏讹,就不會(huì)比較兩者的結(jié)構(gòu),而是直接刪除組件D畜吊,重新創(chuàng)建組件G及其子節(jié)點(diǎn)泽疆。雖然當(dāng)兩個(gè)組件是不同類型但結(jié)構(gòu)相似時(shí),進(jìn)行diff算法分析會(huì)影響性能玲献,但是畢竟不同類型的組件存在相似DOM樹的情況在實(shí)際開發(fā)過程中很少出現(xiàn)殉疼,因此這種極端因素很難在實(shí)際開發(fā)過程中造成重大影響梯浪。
4.element diff
當(dāng)節(jié)點(diǎn)屬于同一層級(jí)時(shí),diff提供了3種節(jié)點(diǎn)操作株依,分別為INSERT_MARKUP(插入)驱证,MOVE_EXISTING(移動(dòng)),REMOVE_NODE(刪除)。
INSERT_MARKUP:新的組件類型不在舊集合中恋腕,即全新的節(jié)點(diǎn)抹锄,需要對(duì)新節(jié)點(diǎn)進(jìn)行插入操作。
MOVE_EXISTING:舊集合中有新組件類型荠藤,且element是可更新的類型伙单,這時(shí)候就需要做移動(dòng)操作,可以復(fù)用以前的DOM節(jié)點(diǎn)哈肖。
REMOVE_NODE:舊組件類型吻育,在新集合里也有,但對(duì)應(yīng)的element不同則不能直接復(fù)用和更新淤井,需要執(zhí)行刪除操作布疼,或者舊組件不在新集合里的,也需要執(zhí)行刪除操作币狠。
注:diff算法來源于https://blog.csdn.net/qq_26708777