上一篇文章中,DOM樹的信息能夠用JavaScript對象來表示,反過來,能夠根據這個用JavaScript對象表示的樹結構來真正構建一顆DOM樹。 用JavaScript對象表示DOM信息和結構,當狀態變動的時候,從新渲染這個JavaScript的對象結構,固然這樣作,其實並無更新到真正的頁面上。前端
可是能夠用新渲染的對象樹和舊的樹進行對比,記錄這兩棵樹的差別。記錄下來的不一樣就是咱們須要對頁面真正的DOM操做,而後把它們應用在真正的DOM樹上,頁面纔會真正變動。node
###Virtual DOM算法的實現步驟 1.用JavaScript對象結構表示DOM樹的結構,而後用這個樹構建一個真正的DOM樹,插到文檔中。react
//函數參數的定義,解構賦值中的一個用途 function Element({tagName,props,children}){ if(!(this instanceof Element)){ return new Element({tagName,props,children}); } this.tagName = tagName; this.props = props || {}; this.children = children || []; }
2.當狀態變化的時候,從新構建一顆新的對象樹,而後用新的樹和舊的樹進行比較,記錄兩顆樹的差別。 3.把2所記錄的差別應用到步驟1所構建的真正的DOM樹上,視圖就更新了。 Virtual DOM實質上是在JS和DOM之間作了一個緩存,能夠類比cpu和硬盤,既然硬盤這麼慢,咱們就在它們之間加個緩存,既然DOM這麼慢,就在它們JS和DOM之間加個緩存。CPU(JS)只操做內存(Virtual DOM),最後的時候再把變動寫入硬盤(DOM)。算法
####步驟一:見前一篇博客 ####步驟二:比較兩顆虛擬DOM樹的差別 1.深度優先遍歷,記錄差別 在實際的代碼中,會對新舊兩顆樹進行一個深度優先的遍歷,這樣每一個節點都會有一個惟一的標記。 緩存
在深度優先遍歷的時候,每當遍歷到一個節點的時候就會和新的DOM樹進行比較,若是有差別,就會記錄到一個對象裏面。 2.這裏會涉及到差別類型 (1)替換掉原來的節點,例如把上面的div換成了section (2)移動、刪除、新增子節點 (3)修改節點的屬性 (4)對於文本節點,文本內容可能會改變。dom
####步驟三:把差別引用到真正的DOM樹上 函數
由於步驟一所構建的JavaScript對象樹和render出來的真正的DOM樹的信息、結構是同樣的。因此咱們能夠對那顆DOM樹也進行深度優先遍歷,遍歷的時候從步驟二生成的差別對象中找出當前遍歷的節點差別,而後進行DOM操做。 ####虛擬DOM算法的思想 標準的DOM機制是用戶在應用上的操做實際反映的是直接對真實的dom進行操做,而在React中,用戶在應用中對dom的操做其實是對虛擬dom的操做,用戶的操做產生的數據改變或者state變量改變都會應用到虛擬dom上,以後再批量的對這些更改進行diff算法計算,對比操做先後的虛擬dom樹,把更改後的變化再同步到真實dom上。(在虛擬dom上執行屢次修改,在真實dom中,只會執行一次dom操做,由於在React虛擬dom機制中,它會把全部的操做都合併,只會對比剛開始的狀態和最後操做的狀態,二者中找出不一樣,而後再同步到真實dom中。)this
####react的獨特的diff算法 diff算法的處理方法,對操做先後的dom樹的同一層的節點進行對比,一層一層對比。,這樣時間複雜度就爲o(n)。 code
####比較兩顆虛擬DOM樹的差別對象
在用js對象表示DOM結構後,當頁面狀態發生變化時須要操做DOM的時候,咱們能夠先經過虛擬DOM計算出新的DOM樹和舊的DOM樹之間的最小修改量,而後最後一次性的將差別修改到真實DOM上。 下圖是一個簡單的兩個虛擬DOM之間的差別
可是真實的場景下的DOM結構很複雜,咱們必須藉助於一個有效的DOM樹比較算法。 -(1)如何比較兩個DOM樹
- (2)如何記錄節點之間的差別 #####如何比較兩個DOM樹
計算兩顆樹之間的差別的常規算法複雜度是O(n的3次方),一個文檔的DOM結構有上百個節點是正常的狀況,這種複雜度不能應用於實際項目。因此,針對前端的具體狀況,咱們不多跨級別的修改DOM節點,一般是修改節點的屬性、調整子節點的順序、添加子節點等。因此咱們通常對同級別節點進行比較,避免了diff算法的複雜性。對同級節點進行比較的經常使用方法就是深度優先遍歷。在深度優先遍歷過程當中,每一個節點都會有一個惟一的標記。在深度優先遍歷的時候,每遍歷到一個節點就把該節點和新的樹進行對比。若是有差別就記錄到一個對象裏面。
#####如何記錄節點之間的差別 因爲咱們對DOM採起的是同級比較,所以節點之間的差別能夠歸結爲四種類型
- 修改節點屬性:用PROPS表示
- 修改文本節點內容: 用TEXT表示
- 替換原有節點,用REPLACE表示
- 調整子節點,包括移動、刪除、添加等,用RECODER表示 對於節點之間的差別,能夠很方便的使用上述四種方式進行記錄,好比當舊節點被替換的時候:
{type:REPLACE,node newNode}
當舊節點的屬性修改時
{type:PROPS,props newProps}
每一個節點都有一個編號,若是對應的節點有變化,那麼就把相應變化的類別記錄下來便可。最後將這個差別對真實DOM作最小化的修改。