diff
演算法中的patchvnode
方法,以及核心updatechildren
方法。
在上篇中,我們談到,當vnode不為真實節點,且vnode與oldvnode為同一節點時,會呼叫patchvnode方法。
我們直接從原始碼上進行分析:
// patchvnode()有四個引數
// oldvnode: 舊的虛擬節點
// vnode: 新的虛擬節點
// insertedvnodequeue: 存在於整個patch中,用於收集patch中插入的vnode;
// removeonly: 這個在原始碼裡有提到,removeonly is a special flag used only by也就是說是特殊的flag,用於transition-group元件。
function
patchvnode
(oldvnode, vnode, insertedvnodequeue, removeonly)
// 如果不為同一引用,那說用新的vnode建立了。
// 如果vnode, oldvnode都為靜態節點,且vnode.key === oldvnode.key相等時,當vnode為轉殖節點,或者vnode有v-once指令時,只需把oldvnode對應的真實dom,以及元件例項都複製到vnode上。
if (istrue(vnode.isstatic) &&
istrue(oldvnode.isstatic) &&
vnode.key === oldvnode.key &&
(istrue(vnode.iscloned) || istrue(vnode.isonce)))
// 讓vnode引用到現在的真實dom,當elm修改的時候,會同步修改vnode.elm
const elm = vnode.elm = oldvnode.elm
const oldch = oldvnode.children
const ch = vnode.children
// 我們先patchvnode, 方法就是先呼叫全域性的update hook
// 然後呼叫data裡定義的update hook
if (isdef(data) && ispatchable(vnode))
// 如果vnode.text未定義
// 這裡有個值得注意的地方,具有text屬性的vnode不應該具備有children
// 對於abc123
的寫法應該是
// h('p', ['abc', h('i', '123')])
// 而不是, h('p', 'abc', [h('i', '123')])
// 因此,對text存在與否的情況需單獨拿出來分析
if (isundef(vnode.text)) else
if (isdef(ch)) else
if (isdef(oldch)) else
if (isdef(oldvnode.text))
} else
if (oldvnode.text !== vnode.text)
// 最後再呼叫 postpatch hook。
if (isdef(data))
}
接著說重點 當oldvnode.children與vnode.children都存在,且不相同時呼叫的updatechildren()
方法, 同樣的,咱們從原始碼上分析:
// updatechildren(),有五個引數
// parentelm: oldvnode.elm 的引用
// oldch, newch: 分別是上面分析中的oldvnode.children, vnode.children
// insertedvnodequeue, removeonly 請參考上面。
function
updatechildren
(parentelm, oldch, newch, insertedvnodequeue, removeonly)
// 當oldstartindex > oldendidx 或者 newstartindex > newendidx, 停止遍歷。
while (oldstartidx <= oldendidx && newstartidx <= newendidx) else
if (samevnode(oldstartvnode, newstartvnode)) else
if (samevnode(oldendvnode, newendvnode)) else
if (samevnode(oldstartvnode, newendvnode)) else
if (samevnode(oldendvnode, newstartvnode)) else else
// 如果根據vnode.key找出的elmtomove與newstartvnode值得比較比較
// patchvnode這兩個節點
// 之後,需要把這個child設定為undefined
// 同時需要把oldstartvnode.elm的位置移到newstartvnode.elm之前,以免影響接下來的遍歷。
if (samevnode(elmtomove, newstartvnode)) else }}
}// 遍歷完成之後,存在兩種情況
// 如果 oldstartidx > oldendidx, 即oldch先遍歷完
// 位於 newstartidx與newendidx之間的節點都可認為是新的節點
if (oldstartidx > oldendidx) else
if (newstartidx > newendidx)
}
直接在原始碼上分析,可能有點亂,總結一下:
patchvnode
共有以下情況:
如果vnode
是text node
,改變elm.textcontent
。
patchvnode
有乙個值得注意的地方是,vdom
中規定,具有text
屬性的vnode
不應該具備children
,因此需把text node
單獨拿出來分析。
updatechildren()
方法共有5種比較方式,前四種無key的情況,後一種為有key的情況,當oldstartidx > oldendidx
或者newstartidx > newoldstartidx
的時候停止遍歷。
遍歷完成後,如果oldch先遍歷完,位於newstartidx與newendidx之間的節點都可認為是新的節點,呼叫相應的方法插入節點。如果newch先遍歷完,此時,位於oldstartidx與oldendidx之間的節點已經不存在了,呼叫removevnodes()方法移除節點。
完。
關於一些Vue的文章。(5)
前三篇裡,我們開始從render,template,el的渲染dom樹的優先順序,最終都編譯成render函式,而後得到vnode 虛擬dom 經過diff演算法後,得到真實dom。那麼問題來了?得到真實dom以後接下來該做什麼?以及怎麼做?照例,分享一篇文章,vue。官網,暫時還沒有找到一篇文章能...
關於一些Vue的文章。(7)
還有其他許多,就不一一枚舉出來了。有沒有看上了的?沒有我等下再來問。在這篇文章裡我將是這幾個月來對 vue 學習的乙個小結。vue 和其他的 mvvm 思路是類似的 主要是為了實現三個過程 observer 通過observer對data進行監聽,並且提供訂閱某個資料項的變化的能力。利用object...
關於一些Vue的文章。(5)
前三篇裡,我們開始從render,template,el的渲染dom樹的優先順序,最終都編譯成render函式,而後得到vnode 虛擬dom 經過diff演算法後,得到真實dom。那麼問題來了?得到真實dom以後接下來該做什麼?以及怎麼做?照例,分享一篇文章,vue。官網,暫時還沒有找到一篇文章能...