虛擬DOM簡(jiǎn)介

什么是虛擬DOM?

我們現(xiàn)在使用的三大主流框架Vue.js归粉、Angular和React都是聲明式操作DOM。我們通過描述狀態(tài)和DOM之間的映射關(guān)系是怎樣的,就可以將狀態(tài)渲染成試圖腻扇。關(guān)于狀態(tài)到視圖的轉(zhuǎn)化過程,框架會(huì)幫我們做砾嫉,不需要我們自己去操作DOM幼苛。
狀態(tài)可以是JavaScript中的任意類型。Object焕刮、Array舶沿、String、Number配并、Boolean等都可以作為狀態(tài)括荡,這些狀態(tài)可能最終會(huì)以段落、表單溉旋、鏈接或按鈕等元素呈現(xiàn)在用戶界面上畸冲。
本質(zhì)上,我們將狀態(tài)作為輸入,并生成DOM輸出在頁面上顯示出來邑闲,這個(gè)過程叫做渲染


渲染的過程.PNG

然而通常在程序運(yùn)行時(shí)岩喷,狀態(tài)會(huì)不斷發(fā)生改變(狀態(tài)改變的原因有很多,可能是用戶點(diǎn)擊了某個(gè)按鈕监憎,可能是某個(gè)ajax請(qǐng)求纱意,這些行為都是異步的)每當(dāng)狀態(tài)發(fā)生變化時(shí),都需要重新渲染鲸阔。如何確定狀態(tài)中發(fā)生了什么變化以及需要在哪里更新DOM偷霉?
在這種情況下,最簡(jiǎn)單粗暴的方式是褐筛,不需要關(guān)心狀態(tài)發(fā)生了什么變化类少,不需要關(guān)心哪里更新DOM,我們只要把所有DOM刪除了渔扎,然后使用狀態(tài)重新生成一份DOM硫狞,并將其輸出到界面上。
但是訪問DOM是非常昂貴的晃痴,按照上面的方式残吩,會(huì)造成相當(dāng)多的性能浪費(fèi)。狀態(tài)變化通常只是有限的幾個(gè)節(jié)點(diǎn)需要重新渲染倘核,所有我們不僅需要找出哪里需要更新泣侮,還需要盡可能少的訪問DOM。


狀態(tài)發(fā)生變化.PNG

如上圖所示紧唱,當(dāng)某個(gè)狀態(tài)發(fā)生變化時(shí)活尊,只更新與這個(gè)狀態(tài)相關(guān)聯(lián)的DOM節(jié)點(diǎn)。
這個(gè)問題有很多種解決方案,目前,各大主流框架都有自己一套解決方案懈词,在Angular中就是臟檢查的流程祝高,React中使用虛擬DOM,vuejs1.0通過細(xì)粒度的綁定。因此,虛擬DOM本質(zhì)上只是眾多解決方案中的一種,可以用但并不一定必須用翎苫。
虛擬DOM的解決方式是通過狀態(tài)生成一個(gè)虛擬節(jié)點(diǎn)樹,然后使用虛擬節(jié)點(diǎn)樹進(jìn)行渲染榨了。在渲染之前煎谍,會(huì)使用新生成的虛擬節(jié)點(diǎn)數(shù)和上一次生成的虛擬節(jié)點(diǎn)樹進(jìn)行對(duì)比,只渲染不同的部分龙屉。
虛擬節(jié)點(diǎn)數(shù)其實(shí)是由組件樹建立起來的整個(gè)虛擬節(jié)點(diǎn)(Virtual Node呐粘,也簡(jiǎn)寫為Vnode)樹满俗。


虛擬節(jié)點(diǎn)樹.PNG

為什么要引入虛擬DOM

事實(shí)上,Angular和React的變化偵測(cè)有一個(gè)共同點(diǎn)作岖,那就是他們都不知道哪些狀態(tài)變了唆垃。因此,就需要進(jìn)行比較暴力的對(duì)比痘儡,React是通過虛擬DOM的比對(duì)辕万,Angular是使用臟檢查的流程。
Vue.js的變化偵測(cè)不一樣沉删,它在一定程度上知道具體哪些狀態(tài)發(fā)生了變化渐尿,這樣就可以通過更細(xì)粒度的綁定來更新視圖。也就是說矾瑰,在Vue.js中砖茸,當(dāng)狀態(tài)發(fā)生變化時(shí),它在一定程度上知道哪些節(jié)點(diǎn)使用了這個(gè)狀態(tài)殴穴,從而對(duì)這些節(jié)點(diǎn)進(jìn)行更新操作凉夯,不需要對(duì)比。事實(shí)上采幌,在vue.js 1.0中就是這樣實(shí)現(xiàn)的劲够。
但是這樣做也有一定的代價(jià),因?yàn)榱6忍?xì)植榕,每一個(gè)綁定都會(huì)有一個(gè)對(duì)應(yīng)得watcher來觀察狀態(tài)的變化再沧,這樣就會(huì)有一定的內(nèi)存開銷和追蹤依賴的開銷。當(dāng)狀態(tài)被越多的節(jié)點(diǎn)使用時(shí)尊残,開銷就越大。大型項(xiàng)目來說淤堵,這個(gè)開銷是非常大寝衫。
因此,Vue.js 2.0中選擇了中等粒度的解決方案拐邪,那就是引入了虛擬DOM慰毅。組件級(jí)別是一個(gè)watcher實(shí)例,就是說即便一個(gè)組件內(nèi)有10個(gè)節(jié)點(diǎn)使用了某個(gè)狀態(tài)扎阶,但其實(shí)也只有一個(gè)watcher在觀察這個(gè)狀態(tài)的變化汹胃。所以這個(gè)狀態(tài)發(fā)生變化時(shí),只能通知到組件东臀,然后組件內(nèi)部通過虛擬DOM去進(jìn)行比對(duì)和渲染着饥。

Vue.js 中的虛擬DOM

在vue.js中,我們使用模板來描述狀態(tài)和DOM之間的映射關(guān)系惰赋。Vue.js通過編譯將模板轉(zhuǎn)化為渲染函數(shù)render宰掉,執(zhí)行渲染函數(shù)就可以得到一個(gè)虛擬節(jié)點(diǎn)樹呵哨,使用這個(gè)虛擬節(jié)點(diǎn)樹就可以渲染頁面。


模板轉(zhuǎn)化為視圖.PNG

虛擬DOM的終極目標(biāo)是將虛擬節(jié)點(diǎn)(vnode)渲染到視圖上轨奄。但是如果直接使用虛擬節(jié)點(diǎn)覆蓋舊節(jié)點(diǎn)的話孟害,會(huì)造成很多不必要的DOM操作。
例如一個(gè)ul標(biāo)簽下有很多l(xiāng)i標(biāo)簽挪拟,其中只有一個(gè)li變化挨务,這種情況下如果直接用新的ul替換舊的ul,其實(shí)除了那個(gè)發(fā)生了變化的li節(jié)點(diǎn)之外玉组,其他節(jié)點(diǎn)都不需要重新渲染耘子。
由于DOM操作比較慢,所以這些DOM操作在性能上會(huì)有一定的浪費(fèi)球切。避免這些不必要的DOM操作會(huì)提升很大的性能谷誓。
為了避免不必要的DOM操作,虛擬DOM在虛擬節(jié)點(diǎn)映射到視圖的過程中吨凑,將虛擬節(jié)點(diǎn)和上一次渲染視圖所使用的的舊虛擬節(jié)點(diǎn)(oldVnode)進(jìn)行對(duì)比捍歪。找出真正需要更新的節(jié)點(diǎn)來進(jìn)行DOM操作,可以避免不必要改動(dòng)的DOM鸵钝。
圖中給出了虛擬DOM的整體運(yùn)行流程糙臼,先將vnode和oldVnode做對(duì)比,然后在更新視圖


虛擬DOM執(zhí)行過程.PNG

可以看出虛擬DOM在Vue.js中所做的事情并沒有那么復(fù)雜恩商,他主要做了兩件事
  • 提供與真實(shí)DOM節(jié)點(diǎn)所對(duì)應(yīng)得虛擬節(jié)點(diǎn)vnode
  • 將虛擬節(jié)點(diǎn)vnode和舊虛擬節(jié)點(diǎn)oldvnode進(jìn)行對(duì)比变逃,然后更新視圖。
    vnode是JavaScript中一個(gè)很普通的對(duì)象怠堪,這個(gè)對(duì)象的屬性上保存了生成DOM節(jié)點(diǎn)所需要的一些數(shù)據(jù)揽乱。
    對(duì)比兩個(gè)虛擬節(jié)點(diǎn)是虛擬DOM中最核心的算法(即patch),他可以判斷出哪些節(jié)點(diǎn)發(fā)生了變化粟矿,從而只對(duì)發(fā)生了變化的節(jié)點(diǎn)進(jìn)行操作凰棉。

總結(jié)

虛擬DOM是講狀態(tài)映射成試圖的眾多解決方案之一,它的運(yùn)作原理是使用狀態(tài)生成虛擬節(jié)點(diǎn)陌粹,然后使用虛擬節(jié)點(diǎn)渲染成視圖撒犀。
之所以需要先使用狀態(tài)生成虛擬節(jié)點(diǎn),是因?yàn)槿绻苯佑脿顟B(tài)生成真實(shí)的DOM掏秩,會(huì)有一定程度上的性能浪費(fèi)或舞。而先創(chuàng)建虛擬節(jié)點(diǎn)再渲染視圖,就可以將虛擬節(jié)點(diǎn)緩存蒙幻,然后使用新創(chuàng)建的虛擬節(jié)點(diǎn)和上一次緩存的虛擬節(jié)點(diǎn)進(jìn)行對(duì)比映凳,然后根據(jù)對(duì)比結(jié)果更新需要更新的DOM節(jié)點(diǎn),避免不必要的DOM操作杆煞。
由于Vue.js的變化偵測(cè)粒度更細(xì)魏宽,所以擋狀態(tài)發(fā)生變化時(shí)腐泻,vue.js知道的信息更多,一定程度上知道哪些位置使用了窗臺(tái)队询。因此派桩,vue.js可以通過細(xì)粒度的綁定來更新視圖,vue.js 1.0 就是這樣實(shí)現(xiàn)的蚌斩。
但是這么做也有一定的代價(jià)铆惑。因?yàn)榱6忍?xì),就會(huì)有很多的watcher同時(shí)觀察這些狀態(tài)送膳,會(huì)有一定的內(nèi)存開銷和依賴追蹤依賴的開銷员魏,所以vue.js 2.0 采取了中等粒度的解決方案。狀態(tài)偵測(cè)不再是某個(gè)具體節(jié)點(diǎn)叠聋,而是某個(gè)組件撕阎,組件內(nèi)部通過虛擬DOM來渲染視圖,這樣可以大大的縮減依賴數(shù)量和watcher數(shù)量碌补。
Vue.js中通過模板來描述狀態(tài)和視圖之間的映射關(guān)系虏束,所以會(huì)將模板編譯成渲染函數(shù)render,然后執(zhí)行渲染函數(shù)生成虛擬節(jié)點(diǎn)vnode,最后使用虛擬節(jié)點(diǎn)更新視圖厦章。
虛擬DOM在vue.js中所做的事是將虛擬節(jié)點(diǎn)vnode和舊虛擬節(jié)點(diǎn)oldVnode進(jìn)行對(duì)比镇匀,根據(jù)對(duì)比結(jié)果來進(jìn)行DOM操作來更新視圖。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末袜啃,一起剝皮案震驚了整個(gè)濱河市汗侵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌群发,老刑警劉巖晰韵,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異也物,居然都是意外死亡宫屠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門滑蚯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人抵栈,你說我怎么就攤上這事告材。” “怎么了古劲?”我有些...
    開封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵斥赋,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我产艾,道長(zhǎng)疤剑,這世上最難降的妖魔是什么滑绒? 我笑而不...
    開封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮隘膘,結(jié)果婚禮上疑故,老公的妹妹穿的比我還像新娘。我一直安慰自己弯菊,他們只是感情好纵势,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著管钳,像睡著了一般钦铁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上才漆,一...
    開封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天牛曹,我揣著相機(jī)與錄音,去河邊找鬼醇滥。 笑死黎比,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的腺办。 我是一名探鬼主播焰手,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼怀喉!你這毒婦竟也來了书妻?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤躬拢,失蹤者是張志新(化名)和其女友劉穎躲履,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體聊闯,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡工猜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了菱蔬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片篷帅。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖拴泌,靈堂內(nèi)的尸體忽然破棺而出魏身,到底是詐尸還是另有隱情,我是刑警寧澤蚪腐,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布箭昵,位于F島的核電站,受9級(jí)特大地震影響回季,放射性物質(zhì)發(fā)生泄漏家制。R本人自食惡果不足惜正林,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望颤殴。 院中可真熱鬧觅廓,春花似錦、人聲如沸诅病。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贤笆。三九已至蝇棉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芥永,已是汗流浹背篡殷。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留埋涧,地道東北人板辽。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像棘催,于是被迫代替她去往敵國(guó)和親劲弦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容