源码学习——snabbdom(二)

121 阅读3分钟

前言

上节我们梳理了下init的过程,重点讲解了return的patch函数,其中涉及到的方法大多我们都简单带过了,接下来我们就分析下这些方法。

方法

我们先从patch入手,首先就是updateChildren方法用来更新子节点。

function updateChildren(
parentElm: Node,
oldCh: VNode[],
newCh: VNode[],
insertedVnodeQueue: VNodeQueue
)

上面有四个参数:parentElm父节点,oldCh旧的子节点,newCH新的子节点,insertedVnodeQueue节点队列,参数很简单,具体的源码就很复杂,这里我们还是化繁为简,梳理下大致的流程:

  • 首先就是很多变量的定义,根据名称我们就可以大致知道它们的作用,这也能帮助我们理解后续流程。
    VD_)OF6`6@THGZ8%0RWCHSD.png
  • 接下来就是通过while将新旧节点进行循环对比,利用if进行非空判断,为空时将下标右移,然后就是几种情况的判定。
  • 第一种odlStart与newStart节点相同,通过pachVnode进行更新,然后将startIndex下标统一右移;第二种odlEnd与newEnd同理判断。

8)RW4N2T7[5FQ@@ZZMJ24.png
= 第三种oldStart与newEnd相同,使用insetBefore将oldStart添加到oldEnd后面;第四种oldEnd与newStart相同就将oldEnd添加到oldStart前面。

QQ图片20240716221514.png
这两种情况我们画两个队列一对比就很好理解了,之所以这么做就是为了性能。

  • 上面几种情况是通过sameVnode进行判断,接下来就是利用下标,对节点进行新增。如果newStart在oldStart之前就添加newStart;newEnd在oldEnd之后就在oldEnd添加newEnd,添加的方法都是通过insertBeforecreateElm

7MBBE9UNM3%6$ST{R1XQY.png

  • 下面就是move移除旧的节点,将newStart添加到oldStart的前面,并将需要移除的节点放到oldStart的前面。

}2XE2FP570T_23U391)[K.png

  • 最后根据下标判断添加newVnodes,移除oldVnodes。 QQ图片20240716224253.png
    这就是upDate的过程,vue更新dom的流程也大致如此。upDate过程中也用到了其它方法:
  1. sameVnode:检查两个vnode是否一样,通过对比vnode的key、data等属性得到结果。
const isSameKey = vnode1.key === vnode2.key;
const isSameIs = vnode1.data?.is === vnode2.data?.is;
const isSameSel = vnode1.sel === vnode2.sel;
  1. emptyNodeAt:将一个真实dom节点变成空的虚拟dom,先获取elm的id、classes,通过vnode得到虚拟dom。
return vnode(
api.tagName(elm).toLowerCase() + id + c,
{},
[],
undefined,
elm
);
  1. createRmCb:用来移除子节点,通过removeChild实现。
return function rmCb() {
if (--listeners === 0) {
const parent = api.parentNode(childElm) as Node;
if (parent !== null) {
api.removeChild(parent, childElm);
}
}
};
  1. addVnodes:添加节点,for循环结合insertBefore进行实现。
for (; startIdx <= endIdx; ++startIdx) {
const ch = vnodes[startIdx];
if (ch != null) {
api.insertBefore(parentElm, createElm(ch, insertedVnodeQueue), before);
}
}
  1. removeVnodes:移除节点,也是通过循环节点与createRmCb进行结合实现,具体逻辑要复杂点,根据不同类型会采用不同的方法:
  • text文本节点,利用removeChild进行移除。
  • fragement文档节点,通过invokeDestroyHook方法。
  • 其它类型节点,会先创建createRmCb,通过removeHook进行移除。
    如果当前节点存在子节点,通过递归调用removeVnodes移除所有节点。

QQ图片20240717155335.png
init就是ele与vnode之间的转换更新,具体的流程大家也可以查看源码仔细研究下。

总结

我们解析了一下init过程中的其它操作,重点是更新节点这一过程。不管如何操作虚拟dom最终都会体现到真实dom上,很多方法中都用到了insertBefore、removeChildren等domApi。