1. 為什么需要虛擬DOM
先介紹瀏覽器加載一個網(wǎng)頁需要經(jīng)歷那些過程放坏;我們只討論頁面解析流程军洼,不考慮網(wǎng)絡(luò)請求過程。
瀏覽器內(nèi)核拿到html文件后璧针,大致分為一下5個步驟:
1. 解析html元素,構(gòu)建dom 樹
2. 解析CSS渊啰,生成頁面css規(guī)則樹(Style Rules)
3. 將dom樹 和 css規(guī)則樹關(guān)聯(lián)起來探橱,生成render樹
4. 布局(layout/ reflow)申屹,瀏覽器會為Render樹上的每個節(jié)點確定在屏幕上的尺寸、位置
5. 繪制Render樹隧膏,繪制頁面像素信息到屏幕上哗讥,這個過程叫paint
? ? 當(dāng)你用原生js 或jquery等庫去操作DOM時,瀏覽器會從構(gòu)建DOM樹開始講整個流程執(zhí)行一遍胞枕,所以頻繁操作DOM會引起不需要的計算杆煞,導(dǎo)致頁面卡頓,影響用戶體驗曲稼。而Virtual DOM能很好的解決這個問題索绪。它用javascript對象表示virtual node(VNode),根據(jù)VNode 計算出真實DOM需要做的最小變動贫悄,然后再操作真實DOM節(jié)點瑞驱,提高渲染效率。
2. Virtual DOM
虛擬DOM用javascript對象來表示VNode窄坦,VNode的結(jié)構(gòu)如下:
虛擬節(jié)點(vNode)結(jié)構(gòu)
下面是虛擬DOM的算法流程圖:
虛擬DOM算法流程圖
React Diff算法
高效的diff算法能夠保證進(jìn)行對實際的DOM進(jìn)行最小的變動唤反。但是標(biāo)準(zhǔn)的的 Diff 算法復(fù)雜度需要 O(n^3),這顯然無法滿足性能要求鸭津。要達(dá)到每次界面都可以整體刷新界面的目的彤侍,勢必需要對算法進(jìn)行優(yōu)化。React里結(jié)合 Web 界面的特點做出了兩個簡單的假設(shè)逆趋,使得 Diff 算法復(fù)雜度直接降低到 O(n)盏阶。
1. 兩個相同組件產(chǎn)生類似的 DOM 結(jié)構(gòu),不同的組件產(chǎn)生不同的 DOM 結(jié)構(gòu)闻书;
2. 對于同一層次的一組子節(jié)點名斟,它們可以通過唯一的 id 進(jìn)行區(qū)分。
算法上的優(yōu)化是 React 整個界面 Render 的基礎(chǔ)魄眉,保證了整體界面渲染的性能砰盐。
不同節(jié)點類型的比較
為了在樹之間進(jìn)行比較,我們首先要能夠比較兩個節(jié)點坑律,在 React 中即比較兩個虛擬 DOM 節(jié)點岩梳,當(dāng)兩個節(jié)點不同時,應(yīng)該如何處理晃择。這分為兩種情況:(1)節(jié)點類型不同 冀值,(2)節(jié)點類型相同,但是屬性不同宫屠。
節(jié)點類型不同:直接刪除原節(jié)點池摧, 插入新節(jié)點。
React 的 DOM Diff 算法實際上只會對樹進(jìn)行逐層比較激况,兩棵樹只會對同一層次的節(jié)點進(jìn)行比較如下所述。
dom樹
React 只會對相同顏色方框內(nèi)的 DOM 節(jié)點進(jìn)行比較,即同一個父節(jié)點下的所有子節(jié)點乌逐。當(dāng)發(fā)現(xiàn)節(jié)點已經(jīng)不存在竭讳,則該節(jié)點及其子節(jié)點會被完全刪除掉,不會用于進(jìn)一步的比較浙踢。這樣只需要對樹進(jìn)行一次遍歷绢慢,便能完成整個 DOM 樹的比較。
相同類型節(jié)點的比較
React 會對屬性進(jìn)行重設(shè)從而實現(xiàn)節(jié)點的轉(zhuǎn)換洛波。