前言先來了解幾個點…
1. 當資料發生變化時,vue是怎麼更新節點的?
要知道渲染真實dom的開銷是很大的,比如有時候我們修改了某個資料,如果直接渲染到真實dom上會引起整個dom樹的重繪和重排,有沒有可能我們只更新我們修改的那一小塊dom而不要更新整個dom呢?diff演算法能夠幫助我們。
我們先根據真實dom生成一顆virtual dom,當virtual dom某個節點的資料改變後會生成乙個新的vnode,然後vnode和oldvnode作對比,發現有不一樣的地方就直接修改在真實的dom上,然後使oldvnode的值為vnode。
diff的過程就是呼叫名為patch的函式,比較新舊節點,一邊比較一邊給真實的dom打補丁。
2. virtual dom和真實dom的區別?
virtual dom是將真實的dom的資料抽取出來,以物件的形式模擬樹形結構。比如dom是這樣的:
對應的virtual dom(偽**):
var vnode = ]};
3. diff的比較方式?在採取diff演算法比較新舊節點的時候,比較只會在同層級進行, 不會跨層級比較。
456
上面的**會分別比較同一層的兩個div以及第二層的p和span,但是不會拿div和span作比較。在別處看到的一張很形象的圖:
diff流程圖
當資料發生改變時,set方法會讓呼叫dep.notify通知所有訂閱者watcher,訂閱者就會呼叫patch給真實的dom打補丁,更新相應的檢視。
來看看patch是怎麼打補丁的(**只保留核心部分)
function patch (oldvnode, vnode) else
}// some code
return vnode
}
patch函式接收兩個引數oldvnode和vnode分別代表新的節點和之前的舊節點
function samevnode (a, b)
如果兩個節點都是一樣的,那麼就深入檢查他們的子節點。如果兩個節點不一樣那就說明vnode完全被改變了,就可以直接替換oldvnode。
雖然這兩個節點不一樣但是他們的子節點一樣怎麼辦?別忘了,diff可是逐層比較的,如果第一層不一樣那麼就不會繼續深入比較第二層了。(我在想這算是乙個缺點嗎?相同子節點不能重複利用了…)
patchvnode
當我們確定兩個節點值得比較之後我們會對兩個節點指定patchvnode方法。那麼這個方法做了什麼呢?
patchvnode (oldvnode, vnode) else else if (ch)else if (oldch)}}
這個函式做了以下事情:
updatechildren
**量很大,不方便一行一行的講解,所以下面結合一些示例圖來描述一下。
updatechildren (parentelm, oldch, newch) else if (oldendvnode == null) else if (newstartvnode == null) else if (newendvnode == null) else if (samevnode(oldstartvnode, newstartvnode)) else if (samevnode(oldendvnode, newendvnode)) else if (samevnode(oldstartvnode, newendvnode)) else if (samevnode(oldendvnode, newstartvnode)) else
idxinold = oldkeytoidx[newstartvnode.key]
if (!idxinold)
else else
newstartvnode = newch[++newstartidx]}}
}if (oldstartidx > oldendidx) else if (newstartidx > newendidx)
}
先說一下這個函式做了什麼
**updatechildren
終於來到了這一部分,上面的總結相信很多人也看得一臉懵逼,下面我們好好說道說道。(這都是我自己畫的,求推薦好用的畫圖工具…)
粉紅色的部分為oldch和vch
我們將它們取出來並分別用s和e指標指向它們的頭child和尾child
現在分別對olds、olde、s、e兩兩做samevnode比較,有四種比較方式,當其中兩個能匹配上那麼真實dom中的相應節點會移到vnode相應的位置,這句話有點繞,打個比方
再配個圖
第一步
olds = a, olde = d;
s = a, e = b;
olds和s匹配,則將dom中的a節點放到第乙個,已經是第乙個了就不管了,此時dom的位置為:a b d
第二步
olds = b, olde = d;
s = c, e = b;
olds和e匹配,就將原本的b節點移動到最後,因為e是最後乙個節點,他們位置要一致,這就是上面說的:當其中兩個能匹配上那麼真實dom中的相應節點會移到vnode相應的位置,此時dom的位置為:a d b
第三步
olds = d, olde = d;
s = c, e = d;
olde和e匹配,位置不變此時dom的位置為:a d b
第四步
olds++;
olde--;
olds > olde;
遍歷結束,說明oldch先遍歷完。就將剩餘的vch節點根據自己的的index插入到真實dom中去,此時dom位置為:a c d b
一次模擬完成。
這個匹配過程的結束有兩個條件:
下面再舉乙個例子,可以像上面那樣自己試著模擬一下
當這些節點samevnode成功後就會緊接著執行patchvnode了,可以看一下上面的**
if (samevnode(oldstartvnode, newstartvnode))
就這樣層層遞迴下去,直到將oldvnode和vnode中的所有子節點比對完。也將dom的所有補丁都打好啦。那麼現在再回過去看updatechildren的**會不會容易很多呢? vue的diff演算法
1.當資料發生變化時,vue是怎麼更新節點的?渲染真實dom的開銷很大,比如我們修改了某個資料,如果直接渲染到真實dom會引起整個dom樹的重繪和重排,有沒有可能我們只更新我們修改的那一小塊dom而不更新整個dom呢?diff演算法可以幫助我們。我們根據真實dom生成乙個虛擬dom,當虛擬dom某個...
Vue 虛擬節點及diff演算法詳解
vue進入2.0以來在其內部加入了虛擬dom的實現,減少了dom的操作,極大提高了效能,同時其diff演算法的時間複雜度為o n 效能很高。首先我們來看下什麼是虛擬dom virtual dom 虛擬dom就是提通過js生成乙個dom物件,之後通過diff演算法比較之後生成patch,即補丁,之後虛...
vue的diff演算法學習
原始碼位址 diff演算法首先要明確乙個概念就是diff的物件是虛擬dom,更新真實dom則是diff演算法的結果 constructor 這個部分的 主要是為了更好地知道在diff演算法中具體diff的屬性的含義,當然也可以更好地了解vnode例項 核心函式是patch函式 isundef判斷 是...