Snabbdom 源码解析 - removeVnodes 和 addvnodes

172 阅读3分钟

「这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战

removeVnode 和 addvnodes

本章我们继续进行Snabbdom 源码解析篇章,我们的目标是 removeVnodes 和 addvnodes 函数

这两个函数是在 patch 函数中内部调用的

  • removeVnodes:从DOM树上批量移除VNodes对应的DOM元素
  • addvnodes:从DOM树上批量添加VNodes对应的DOM元素

removeVnodes 函数解析

理解一个函数就要先从他的参数开始理解

参数解析

  • parentElm:要删除的元素所在的父元素
  • vnodes: 这个参数是一个数组,存储的时候要删除的DOM元素对应的VNode
  • startIdx: 数组中要删除的节点的起始位置
  • endIdx: 数组中要删除的节点的结束位置

代码解析

for (; startIdx <= endIdx; ++startIdx) {
      let listeners: number
      let rm: () => void
      const ch = vnodes[startIdx]
  • startIdx <= endIdx; ++startIdx这里是根据用户传入的参数,对要截取的点
  • const ch = vnodes[startIdx] 找到要删除的元素节点

接着

 if (ch != null) {
 ...
}

这一步的判断就很简单了,是否有要被删除的元素节点

如果有要被删除的元素节点,那么紧接着

if (isDef(ch.sel)) {
   ....
} else { // Text node
  api.removeChild(parentElm, ch.elm!)
}

根据元素的sel属性判断是否元素节点or文本节点

  • 如果 ch.sel !== undefined 元素节点
  • 如果 ch.sel === undefined 文本节点

如果被删除的节点是元素节点的话

...
invokeDestroyHook(ch)
listeners = cbs.remove.length + 1
rm = createRmCb(ch.elm!, listeners)
for (let i = 0; i < cbs.remove.length; ++i) cbs.remove[i](ch, rm)
const removeHook = ch?.data?.hook?.remove
if (isDef(removeHook)) {
    removeHook(ch, rm)
} else {
    rm()
}
  • invokeDestroyHook: 触发了 destroy 的钩子函数
  • listeners = cbs.remove.length + 1获取cbs.remove的钩子函数 并 +1,这个变量的作用是防止变量重复删除DOM元素
  • rm = createRmCb(ch.elm!, listeners)createRmCb是个高阶函数,他是一个返回真正删除DOM元素的函数
    • ch.elm: 要被删除的元素
    • listeners: 防止变量重复删除DOM元素
  • for (let i = 0; i < cbs.remove.length; ++i) cbs.remove[i](ch, rm): 遍历remove方法,然后传入chrm(也就是createRmCb返回的函数)
  • const removeHook = ch?.data?.hook?.remove:这步主要是获取用户传入的钩子函数,如果有传入remove钩子函数返回该函数,没有返回 undefined
  • 最后这个if节点就是判断removeHook是否存在,然后执行对应的方法

如果被删除的节点是文本节点的话

我们会直接调用 api.removeChild(parentElm, ch.elm!) 删除父元素中的elm属性

到这里我们的 removeVnodes就完结了

addVnodes 函数

function addVnodes (
    parentElm: Node,
    before: Node | null,
    vnodes: VNode[],
    startIdx: number,
    endIdx: number,
    insertedVnodeQueue: VNodeQueue
  ) {
    for (; startIdx <= endIdx; ++startIdx) {
      const ch = vnodes[startIdx]
      if (ch != null) {
        api.insertBefore(parentElm, createElm(ch, insertedVnodeQueue), before)
      }
    }
  }

参数:

  • parentElm: 父元素
  • before: 参考节点
  • vnodes:添加的节点
  • startIdx、endIdx: 开始结束节点
  • insertedVnodeQueue: 存储刚刚插入的具有inserted钩子函数的节点

接下来的代码就很简单了 通过createElm(ch, insertedVnodeQueue)创建DOM元素,插入到DOM树种,完结

算了 我这种傻*文章也不要赞了。。。 我自己都觉得很傻,在总结什么也不知道