day101-vuejs源码-patch-笔记

前言

本文主要是记录下读vuejs源码中virtualDom部分,注释一小部分的patch核心diff算法。算法通过同层的树节点进行比较而非对树进行逐层搜索遍历的方式,所以时间复杂度只有O(n),是一种相当高效的算法
下面截取的代码部分是createPatchFunction(一个patch函数)的返回值,

oldValue 与 vnode在sameVnode的时候才会进行patchVnode。

代码


return function patch(oldValue, vnode, hydrating, removeOnly, parentElm, refElm) {
// vnode不存在则直接销毁钩子
if (isUndef(vnode)) {
if (isDef(oldVnode)) invokeDestroyHook(oldValue)
return
}
let isInitialPatch = false
const insertedVnodeQueue = []
if (isUndef(oldVnode)) {
// 空挂载(像是组件),创建新的root节点
// oldValue未定义时,即为root节点时,创建一个新节点
isInitialPatch = true
createElm(vnode, insertedVnodeQueue, parentElm, refElm)
} else {
// 标记旧的VNode是否有nodeType ??
const isRealElement = isDef(oldVnode, vnode)
if (!isRealElement && sameVnode(oldValue, vnode)) {
patchVnode(oldValue, vnode, insertedVnodeQueue, removeOnly)
} else {
if (isRealElement) {
// 挂载到一个真的节点
// 检查是否是服务端渲染的内容且if we can perform a successful hydration(是否我们可以合并到真实DOM)
if (oldVnode.nodeType === 1 && oldValue.hasAttribute(SSR_ATTR)) {
oldVnode.removeAttribute(SSR_ATTR)
hydrating = true
}
if (isTrue(hydrating)) {
// 需要合并到真实DOM
if(hydrate(oldVnode, vnode, insertedVnodeQueue)) {
invokeInsertHook(vnode, insertedVnodeQueue, true)
return oldValue
} else if (ProcessingInstruction.env.NODE_ENV !== 'production') {
warn(
'The client-side rendered virtual DOM tree is not matching ' +
'server-rendered content. This is likely caused by incorrect ' +
'HTML markup, for example nesting block-level elements inside ' +
'<p>, or missing <tbody>. Bailing hydration and performing ' +
'full client-side render.'
)
}
}
// 如果不是服务端渲染或者合并到真实DOM失败,则创建一个空的VNode节点替换它
oldValue = emptyNodeAt(oldVnode)
}
// 取代现有元素
const oldElm = oldValue.elm
const parentElm = nodeOps.parentNode(oldElm)
createElm(
vnode,
insertedVnodeQueue, // ???
// extremely rare edge case: do not insert if old element is in a
// leaving transition. Only happens when combining transition +
// keep-alive + HOCs. (#4590)
oldElm._leaveCb ? null : parentElm,
nodeOps.nextSibling(oldElm)
)
if (isDef(vnode.parent)) {
// 组件根节点被替换,遍历更新父节点element
let ancestor = vnode.parent
while (ancestor) {
ancestor.elm = vnode.elm
ancestor = ancestor.parent
}
if (isPatchable(vnode)) {
// 调用create回调
for (let i = 0; i < cbs.create.length; ++i) {
cbs.create[i](emptyNode, vnode.parent)
}
}
}
if (isDef(parentElm)) {
// 移除老节点
removeVnodes(parentElm, [oldValue], 0, 0)
} else if (isDef(oldValue.tag)) {
invokeDestroyHook(oldVnode)
}
}
}
invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)
return vnode.elm
}

参考

learnVue

文章作者: lmislm
文章链接: http://lmislm.com/2019/04/23/2019-04-23/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 LMISLMのBlog