「这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战」
patchVnode
接下来,我们要进行patch函数中最后一个函数patchVnode的代码解析了
作用
patchVnode函数主要的作用是对比新旧两个节点,然后对比两个节点的差异,然后更新到真实DOM上,下面我们先来看这个函数的整体执行过程
调用
在调用 patchVnode 函数时候,我们先使用了 sameVnode函数判断两个vnode元素是否是相同的元素
if (sameVnode(oldVnode, vnode)) {
patchVnode(oldVnode, vnode, insertedVnodeQueue)
}
sameVnode函数代码十分的简单
function sameVnode (vnode1: VNode, vnode2: VNode): boolean {
return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel
}
就是判断新旧两个节点的key、sel是否相同
patchVnode函数
- 参数
- oldVnode: 旧节点
- vnode: 新节点
- insertedVnodeQueue: 收集具有inserted钩子函数的节点
- 函数解析: 这个函数内部代码比较多,我们先来看一下整体的过程
- 触发
perpatch和update钩子函数- 判断用户是否有传入钩子函数,如果用户传入了
prepatch钩子函数,会立即执行const hook = vnode.data?.hook hook?.prepatch?.(oldVnode, vnode) const elm = vnode.elm = oldVnode.elm!把就旧节点的elm属性赋值给新节点的elm属性,然后分别获取新旧vnode的子节点const elm = vnode.elm = oldVnode.elm! const oldCh = oldVnode.children as VNode[] const ch = vnode.children as VNode[]- 判断新旧子节点是否相同节点
if (oldVnode === vnode) return - 新旧子节点不是相同节点时候,且data有值,执行
update函数if (vnode.data !== undefined) { for (let i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode) vnode.data.hook?.update?.(oldVnode, vnode) // 后执行用户传入的钩子函数,是为了让用户的修改事件不被覆盖 }
- 判断用户是否有传入钩子函数,如果用户传入了
- 真正对比新旧 vnode 差异的地方,当找到差异之后会立即更新真实
DOM- 判断新节点是否有 text 属性
if (isUndef(vnode.text)) { - 判断新旧节点是否有子节点,并更新DOM
if (isDef(oldCh) && isDef(ch)) { if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue) } else if (isDef(ch)) { if (isDef(oldVnode.text)) api.setTextContent(elm, '') addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue) } else if (isDef(oldCh)) { removeVnodes(elm, oldCh, 0, oldCh.length - 1) } else if (isDef(oldVnode.text)) { api.setTextContent(elm, '') } ``` - 如果 新节点有 text 属性,比较新旧节点的text属性是否不相同,相同则不进行任何操作 ```js } else if (oldVnode.text !== vnode.text) { ``` - 判断老节点是否有子节点,有的话调用`removeVnodes`移除该节点 ```js if (isDef(oldCh)) { removeVnodes(elm, oldCh, 0, oldCh.length - 1) } api.setTextContent(elm, vnode.text!) // 更新dom元素 } ```
- 判断新节点是否有 text 属性
- 触发
postpatch钩子函数hook?.postpatch?.(oldVnode, vnode)