首先,我们要知道什么情况下才会进行精细化比较:
- 只有是同一个虚拟节点,才进行精细化比较;
- 只进行同层比较,不会进行跨层比较。
上树的前提是此节点必须是虚拟dom节点,因此在上树前要进行虚拟dom判断,如果是真实dom节点,那就先将其包装为虚拟节点:
if(oldVnode.sel == '' || oldVnode.sel == underfined){
oldVnode = vnode(oldVnode.tagName.toLowerCase(), {}, [], underfined, oldVnode);
}
上树的另一个条件是两个节点是同一个节点,如果是同一个节点,进行diff精细化比较,如果不是同一个节点,那就暴力插入新的,删除旧的节点:
if(oldVnode.key == newVnode.key && oldVnode.sel == newVnode.sel){
// 是同一个节点,进行diff精细化比较
}else{
// 暴力插入新的,删除旧的节点
}
暴力插入新的,删除旧的节点
假设我们现在要做的操作时暴力插入新的节点,那我们此时就需要一个新的函数createElement专门做上树的操作,这个函数接收要插入的虚拟节点vnode,返回真实dom节点,patch函数将其返回值插入到旧节点上,完成上树。
这里需要注意的是:在patch函数执行过程中,免不了出现嵌套的虚拟dom结构,那么如何利用递归实现嵌套的虚拟dom节点真实dom化就成了本算法中的重点。
在createElement函数中将传入的虚拟dom节点分为五嵌套结构的和有嵌套结构的,无嵌套结构的会直接创建出真实dom节点并加入文字等内容;有嵌套结构的则会循环遍历其中的children属性,并将其每一个children调用createElement函数实现递归,最后appendChild收集起来返回给patch函数。
同一个节点的精细化比较
在进行精细化比较之前,我们还要处理几种特殊情况。
当前节点进来后,我们首先要判断这是不是和老的节点一模一样,也就是在内存中就是相等的(判断 的时候用===),如果是相等的,那就不需要往下比较了,该节点并没有变化;
如果这里新老节点不相同,那就要进行比较了,但是这里我们需要判断一下新节点是否存在text属性,如果存在那就代表这个新的节点内部只有文字,我们只需要改掉老节点内部的text属性;
当新老节点不同并且新节点内部不存在text属性(新节点有Children),那我们就要知道老节点是否有text属性(老节点是否有Children),如果有text,那就代表这个节点已经是最底部的节点了,我们只需要把新节点放在这即可;
如果新老节点都存在Children,那就是最麻烦的情况(五角星),此时就要进行diff精细化比较了。
参考尚硅谷邵老师教程