Vue之虛擬DOM和diff算法

首先介紹一下snabbdom,snabbdom是著名的虛擬DOM庫泻肯,是diff算法的鼻祖郭卫,Vue源碼借鑒了snabbdom。

1页藻、什么是虛擬DOM

虛擬DOM是用JavaScript對象描述DOM的層次結(jié)構(gòu)桨嫁。DOM中的一切屬性都在虛擬DOM中有對應(yīng)的屬性。本質(zhì)上是JS 和 DOM 之間的一個(gè)映射緩存份帐。虛擬DOM就是為了提高頁面渲染性能璃吧。

要點(diǎn):虛擬 DOM 是 JS 對象;虛擬 DOM 是對真實(shí) DOM 的描述废境。

為什么要使用虛擬DOM畜挨?

用JS對象模擬DOM節(jié)點(diǎn)的好處是筒繁,頁面的更新可以先全部反映在JS對象(虛擬DOM)上,操作內(nèi)存中的JS對象的速度顯然要更快巴元,等更新完成后毡咏,再將最終的JS對象映射成真實(shí)的DOM,交由瀏覽器去繪制逮刨。

diff發(fā)生在虛擬DOM上呕缭。diff算法是在新虛擬DOM和老虛擬DOM進(jìn)行diff(精細(xì)化比對),實(shí)現(xiàn)最小量更新修己,最后反映到真正的DOM上恢总。

1)最小量更新。key是vnode節(jié)點(diǎn)的唯一標(biāo)識睬愤,告訴了diff算法片仿,更改前后他們是同一個(gè)節(jié)點(diǎn)。

2)只有是同一個(gè)虛擬節(jié)點(diǎn)尤辱,才進(jìn)行精細(xì)化比較砂豌,否則就暴力刪除舊的、插入新的光督。

3)只進(jìn)行同層比較阳距,不會進(jìn)行跨層比較。

2可帽、h函數(shù)

我們前面知道diff算法發(fā)生在虛擬DOM上娄涩,而虛擬DOM是如何實(shí)現(xiàn)的呢?實(shí)際上虛擬DOM是有一個(gè)個(gè)虛擬節(jié)點(diǎn)組成映跟。

h函數(shù)用來產(chǎn)生虛擬節(jié)點(diǎn)(vnode)蓄拣。虛擬節(jié)點(diǎn)有如下的屬性:

1)sel: 標(biāo)簽類型,例如 p努隙、div球恤;

2)data: 標(biāo)簽上的數(shù)據(jù),例如 style荸镊、class咽斧、data-*;

3)children :子節(jié)點(diǎn)躬存;

4) text: 文本內(nèi)容张惹;

5)elm:虛擬節(jié)點(diǎn)綁定的真實(shí) DOM 節(jié)點(diǎn);

通過h函數(shù)的嵌套岭洲,從而得到虛擬DOM樹宛逗。

?我們編寫了一個(gè)低配版的h函數(shù),必須傳入3個(gè)參數(shù)盾剩,重載較弱雷激。

?*?形態(tài)1:h('div',?{},?'文字')

?*?形態(tài)2:h('div',?{},?[])

?*?形態(tài)3:h('div',?{},?h())

首先定義vnode節(jié)點(diǎn)替蔬,實(shí)際上就是把傳入的參數(shù)合成對象返回。

然后編寫h函數(shù)屎暇,根據(jù)第三個(gè)參數(shù)的不同進(jìn)行不同的響應(yīng)承桥。

3、patch函數(shù)

如何定義是同一個(gè)節(jié)點(diǎn)呢根悼?舊節(jié)點(diǎn)的key要和新節(jié)點(diǎn)的key相同選擇器也相同凶异。

當(dāng)調(diào)用patch函數(shù)時(shí),會傳入舊番挺、新節(jié)點(diǎn)唠帝。首先我們判斷舊節(jié)點(diǎn)oldVnode是否是虛擬節(jié)點(diǎn)屯掖,如果傳入的不是虛擬節(jié)點(diǎn)是DOM節(jié)點(diǎn)玄柏,我們需要進(jìn)行包裝成虛擬節(jié)點(diǎn),即調(diào)用vnode函數(shù)進(jìn)行轉(zhuǎn)化贴铜。

然后判斷oldVnode和newVnode是不是同一個(gè)節(jié)點(diǎn)粪摘,如果是則進(jìn)行精細(xì)化比較(這個(gè)后面再完成);若不是绍坝,則將新節(jié)點(diǎn)創(chuàng)建為DOM徘意,然后添加到頁面中,并移除舊節(jié)點(diǎn)轩褐。

patch函數(shù)

createElement函數(shù)用來創(chuàng)建節(jié)點(diǎn)椎咧,將vnode節(jié)點(diǎn)創(chuàng)建為DOM。若vnode節(jié)點(diǎn)中存在嵌套把介,我們需要遞歸調(diào)用createElement完成子節(jié)點(diǎn)的創(chuàng)建勤讽。若為文本或undefined,則為直接轉(zhuǎn)換為DOM拗踢。

createElement函數(shù)

如果oldVnode和newVnode是同一個(gè)節(jié)點(diǎn)脚牍,我們需要繼續(xù)進(jìn)行判斷比較。首先判斷oldVnode和newVnode是同一個(gè)對象巢墅,是則什么都不做诸狭,若不是,

判斷newVnode有沒有text屬性君纫,有則判斷text相不相同驯遇,不同則用新的text屬性代替;

若newVnode沒有text屬性蓄髓,意味著有newVnode有children叉庐,再判斷oldVnode有沒有children,沒有(意味著有oldVnode有text)双吆,清空oldVnode的text眨唬,將newVnode的children添加到DOM中会前;

若oldVnode有children,則需要進(jìn)行diff了(后面再續(xù))匾竿。

4瓦宜、diff算法

當(dāng)我們進(jìn)行比較的過程中,我們采用的4種命中查找策略:

1)新前與舊前:命中則指針同時(shí)往后移動岭妖。

2)新后與舊后:命中則指針同時(shí)往前移動临庇。

3)新后與舊前:命中則涉及節(jié)點(diǎn)移動,那么新后指向的節(jié)點(diǎn)昵慌,移到舊后之后假夺。

4)新前與舊后:命中則涉及節(jié)點(diǎn)移動,那么新前指向的節(jié)點(diǎn)斋攀,移到舊前之前已卷。

命中上述4種一種就不在命中判斷了,如果沒有命中淳蔼,就需要循環(huán)來尋找侧蘸,移動到舊前之前。直到while(新前<=新后&&舊前<=就后)不成立則完成鹉梨。

如果是新節(jié)點(diǎn)先循環(huán)完畢讳癌,如果老節(jié)點(diǎn)中還有剩余節(jié)點(diǎn)(舊前和舊后指針中間的節(jié)點(diǎn)),說明他們是要被刪除的節(jié)點(diǎn)存皂。

如果是舊節(jié)點(diǎn)先循環(huán)完畢晌坤,說明新節(jié)點(diǎn)中有要插入的節(jié)點(diǎn)。

當(dāng)新老VNode節(jié)點(diǎn)的start相同時(shí)旦袋,直接patchVnode骤菠,同時(shí)新老VNode節(jié)點(diǎn)的開始索引都加 1

當(dāng)新老VNode節(jié)點(diǎn)的end相同時(shí),同樣直接patchVnode猜憎,同時(shí)新老VNode節(jié)點(diǎn)的結(jié)束索引都減 1

當(dāng)老VNode節(jié)點(diǎn)的start和新VNode節(jié)點(diǎn)的end相同時(shí)娩怎,這時(shí)候在patchVnode后,還需要將當(dāng)前真實(shí)dom節(jié)點(diǎn)移動到oldEndVnode的后面胰柑,同時(shí)老VNode節(jié)點(diǎn)開始索引加 1截亦,新VNode節(jié)點(diǎn)的結(jié)束索引減 1

當(dāng)老VNode節(jié)點(diǎn)的end和新VNode節(jié)點(diǎn)的start相同時(shí),這時(shí)候在patchVnode后柬讨,還需要將當(dāng)前真實(shí)dom節(jié)點(diǎn)移動到oldStartVnode的前面崩瓤,同時(shí)老VNode節(jié)點(diǎn)結(jié)束索引減 1,新VNode節(jié)點(diǎn)的開始索引加 1

如果都不滿足以上四種情形踩官,那說明沒有相同的節(jié)點(diǎn)可以復(fù)用却桶,則會分為以下兩種情況:

從舊的VNode為key值,對應(yīng)index序列為value值的哈希表中找到與newStartVnode一致key的舊的VNode節(jié)點(diǎn),再進(jìn)行patchVnode颖系,同時(shí)將這個(gè)真實(shí)dom移動到oldStartVnode對應(yīng)的真實(shí)dom的前面

調(diào)用createElm創(chuàng)建一個(gè)新的dom節(jié)點(diǎn)放到當(dāng)前newStartIdx的位置嗅剖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市嘁扼,隨后出現(xiàn)的幾起案子信粮,更是在濱河造成了極大的恐慌,老刑警劉巖趁啸,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件强缘,死亡現(xiàn)場離奇詭異,居然都是意外死亡不傅,警方通過查閱死者的電腦和手機(jī)旅掂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來访娶,“玉大人商虐,你說我怎么就攤上這事≌鸢梗” “怎么了称龙?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長戳晌。 經(jīng)常有香客問我,道長痴柔,這世上最難降的妖魔是什么沦偎? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮咳蔚,結(jié)果婚禮上豪嚎,老公的妹妹穿的比我還像新娘。我一直安慰自己谈火,他們只是感情好侈询,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著糯耍,像睡著了一般扔字。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上温技,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天革为,我揣著相機(jī)與錄音,去河邊找鬼舵鳞。 笑死震檩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播抛虏,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼博其,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了迂猴?” 一聲冷哼從身側(cè)響起贺奠,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎错忱,沒想到半個(gè)月后儡率,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡以清,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年儿普,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掷倔。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡眉孩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出勒葱,到底是詐尸還是另有隱情浪汪,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布凛虽,位于F島的核電站死遭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏凯旋。R本人自食惡果不足惜呀潭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望至非。 院中可真熱鬧钠署,春花似錦、人聲如沸荒椭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽趣惠。三九已至狸棍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間信卡,已是汗流浹背隔缀。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留傍菇,地道東北人猾瘸。 一個(gè)月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親牵触。 傳聞我的和親對象是個(gè)殘疾皇子疆偿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

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