?上一篇講到當(dāng)與頁(yè)面渲染相關(guān)的依賴(lài)發(fā)生變化時(shí)巨缘,就會(huì)觸發(fā)render watcher的run方法執(zhí)行,重新收集依賴(lài)捌肴,而render watcher是把updateComponent方法作為watcher的getter爬范,因此每次頁(yè)面渲染所需的數(shù)據(jù)發(fā)生改變時(shí)行疏,都會(huì)執(zhí)行updateComponent方法。
updateComponent方法很簡(jiǎn)單
updateComponent = function () {
vm._update(vm._render())
}
vm._render方法上一篇已經(jīng)說(shuō)過(guò)了畔裕,返回的是一個(gè)根據(jù)render函數(shù)生成的vnode對(duì)象衣撬,這里主要看一下vm._update以及相關(guān)的方法。
Vue.prototype._update = function (vnode) {
var vm = this
var prevVnode = vm._vnode
vm._vnode = vnode
if (!prevVnode) { // 首次渲染
vm.$el = this.__patch__(vm.$el, vnode)
} else {
vm.$el = this.__patch__(prevVnode, vnode)
}
}
// 掛載扮饶、更新vnode到頁(yè)面上
Vue.prototype.__patch__ = function (oldVnode, vnode, parentElm, refElm) {
var isRealElement = oldVnode.nodeType
if (!isRealElement && sameVnode(oldVnode, vnode)) {
patchVnode(oldVnode, vnode)
} else {
// 若isRealElement為true具练,說(shuō)明傳入的oldVnode為真實(shí)的dom節(jié)點(diǎn),則生成一個(gè)空vnode實(shí)例甜无,其中vnode.elm為傳入的dom
if (isRealElement) {
oldVnode = emptyNodeAt(oldVnode)
}
var oldElm = oldVnode.elm
var parentElm = nodeOps.parentNode(oldElm)
createElm(vnode, parentElm, nodeOps.nextSibling(oldElm))
// 移除老的dom元素
removeVnodes(parentElm, [oldVnode], 0, 0)
}
}
// 創(chuàng)建真實(shí)dom扛点,refElm為插入在哪個(gè)元素之前
function createElm (vnode, parentElm, refElm) {
var data = vnode.data
// 創(chuàng)建節(jié)點(diǎn)
if (vnode.tag) {
vnode.elm = nodeOps.createElement(vnode.tag)
// 創(chuàng)建子節(jié)點(diǎn)
createChildren(vnode, vnode.children)
// 添加attr
if (data) {
updateAttrs(emptyNode, vnode)
}
// 插入到文檔中
insert(parentElm, vnode.elm, refElm)
} else {
vnode.elm = nodeOps.createTextNode(vnode.text)
insert(parentElm, vnode.elm, refElm)
}
}
大概邏輯是這樣的:
1.根據(jù)vm實(shí)例上是否有_vnode屬性判斷是否是首次渲染哥遮,若是首次渲染,則傳給patch方法的是要被掛載到的dom元素陵究,以及當(dāng)前的vnode眠饮。
2.在patch方法中,若傳入的oldVnode是一個(gè)真實(shí)的dom铜邮,則為這個(gè)dom元素生成一個(gè)vnode對(duì)象(vnode.elm傳入的dom節(jié)點(diǎn))仪召,接下來(lái)的處理和新舊兩個(gè)vnode不是同一個(gè)vnode的情況相同,插入新的vnode到文檔中松蒜,并且刪除舊的vnode扔茅。
附上本文詳細(xì)的代碼注釋與demo(相關(guān)代碼)