當(dāng) vm 上有屬性改變時(shí),例如 vm.a
由 '111' 改為 '222'纬纪,
在
a
的 set 方法中蚓再,dep.notify()
會(huì)對(duì) 訂閱 a 的 watcher 進(jìn)行更新。vm 的 watcher 訂閱了 vm 上所有的屬性包各。這是會(huì)通知 watcher 的 get 方法摘仅,
vm watcher 的 get 方法 就是updateComponent
updateComponent = function () {
vm._update(vm._render(), hydrating);
};
vm._render
重新生成一次 vnode,這個(gè)新的 vnode 就包含了 對(duì) a 的更改髓棋,_update
調(diào)用vm.$el = vm.__patch__(prevVnode, vnode);
更新 vnodepatch 方法中會(huì)根據(jù) oldVnode 和 vnode 判斷是需要更新实檀,執(zhí)行
patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly);
-
patchVnode算法是:
- 如果oldVnode跟vnode完全一致惶洲,那么不需要做任何事情
- 如果oldVnode跟vnode都是靜態(tài)節(jié)點(diǎn)按声,且具有相同的key,當(dāng)vnode是克隆節(jié)點(diǎn)或是v-once指令控制的節(jié)點(diǎn)時(shí)恬吕,只需要把oldVnode.elm和oldVnode.child都復(fù)制到vnode上签则,也不用再有其他操作
- 否則,如果vnode不是文本節(jié)點(diǎn)或注釋節(jié)點(diǎn)
- 如果oldVnode和vnode都有子節(jié)點(diǎn)铐料,且2方的子節(jié)點(diǎn)不完全一致渐裂,就執(zhí)行更新子節(jié)點(diǎn)的操作(這一部分其實(shí)是在updateChildren函數(shù)中實(shí)現(xiàn)),算法如下
(1)分別獲取oldVnode和vnode的firstChild钠惩、lastChild柒凉,賦值給oldStartVnode、oldEndVnode篓跛、newStartVnode膝捞、newEndVnode
(2)如果oldStartVnode和newStartVnode是同一節(jié)點(diǎn),調(diào)用patchVnode進(jìn)行patch愧沟,然后將oldStartVnode和newStartVnode都設(shè)置為下一個(gè)子節(jié)點(diǎn)蔬咬,重復(fù)上述流程
(3)如果oldEndVnode和newEndVnode是同一節(jié)點(diǎn),調(diào)用patchVnode進(jìn)行patch沐寺,然后將oldEndVnode和newEndVnode都設(shè)置為上一個(gè)子節(jié)點(diǎn)林艘,重復(fù)上述流程
(4)如果oldStartVnode和newEndVnode是同一節(jié)點(diǎn),調(diào)用patchVnode進(jìn)行patch混坞,如果removeOnly是false狐援,那么可以把oldStartVnode.elm移動(dòng)到oldEndVnode.elm之后,然后把oldStartVnode設(shè)置為下一個(gè)節(jié)點(diǎn),newEndVnode設(shè)置為上一個(gè)節(jié)點(diǎn)啥酱,重復(fù)上述流程
(5)如果newStartVnode和oldEndVnode是同一節(jié)點(diǎn)场钉,調(diào)用patchVnode進(jìn)行patch,如果removeOnly是false懈涛,那么可以把oldEndVnode.elm移動(dòng)到oldStartVnode.elm之前逛万,然后把newStartVnode設(shè)置為下一個(gè)節(jié)點(diǎn),oldEndVnode設(shè)置為上一個(gè)節(jié)點(diǎn)批钠,重復(fù)上述流程
(6)如果以上都不匹配宇植,就嘗試在oldChildren中尋找跟newStartVnode具有相同key的節(jié)點(diǎn),如果找不到相同key的節(jié)點(diǎn)埋心,說(shuō)明newStartVnode是一個(gè)新節(jié)點(diǎn)指郁,就創(chuàng)建一個(gè),然后把newStartVnode設(shè)置為下一個(gè)節(jié)點(diǎn)
(7)如果上一步找到了跟newStartVnode相同key的節(jié)點(diǎn)拷呆,那么通過(guò)其他屬性的比較來(lái)判斷這2個(gè)節(jié)點(diǎn)是否是同一個(gè)節(jié)點(diǎn)闲坎,如果是,就調(diào)用patchVnode進(jìn)行patch茬斧,如果removeOnly是false腰懂,就把newStartVnode.elm插入到oldStartVnode.elm之前,把newStartVnode設(shè)置為下一個(gè)節(jié)點(diǎn)项秉,重復(fù)上述流程
(8)如果在oldChildren中沒(méi)有尋找到newStartVnode的同一節(jié)點(diǎn)绣溜,那就創(chuàng)建一個(gè)新節(jié)點(diǎn),把newStartVnode設(shè)置為下一個(gè)節(jié)點(diǎn)娄蔼,重復(fù)上述流程
(9)如果oldStartVnode跟oldEndVnode重合了怖喻,并且newStartVnode跟newEndVnode也重合了,這個(gè)循環(huán)就結(jié)束了
- 如果只有oldVnode有子節(jié)點(diǎn)岁诉,那就把這些節(jié)點(diǎn)都刪除
- 如果只有vnode有子節(jié)點(diǎn)锚沸,那就創(chuàng)建這些子節(jié)點(diǎn)
- 如果oldVnode和vnode都沒(méi)有子節(jié)點(diǎn),但是oldVnode是文本節(jié)點(diǎn)或注釋節(jié)點(diǎn)涕癣,就把vnode.elm的文本設(shè)置為空字符串
- 如果vnode是文本節(jié)點(diǎn)或注釋節(jié)點(diǎn)哗蜈,但是vnode.text != oldVnode.text時(shí),只需要更新vnode.elm的文本內(nèi)容就可以
- 在 patchVNode 中會(huì)執(zhí)行到 updat 的生命周期函數(shù)属划,進(jìn)行 vnode 屬性的更新恬叹,更新會(huì)直接通知到 native。
參考:https://segmentfault.com/a/1190000008291645#articleHeader3