vue3 | 搞懂diff算法

264 阅读2分钟

讲diff算法前,我们先来理解下什么的虚拟dom把,这样会方便我们对diff算法的理解 什么是vnode ?
virtual Node虚拟节点,无论是组件还是元素,他们最终在vue中表示出来的都是一个个VNode,Vnode的本质是描述dom的javaScript对象。

<div class="title" style="font-size: 30px;color:'hotpink"> hellow</div>

上面一段代码的虚拟节点vnode是什么样呢?

const vnode={
            type:'div',
            props:{
                class:"title",
                style:{
                    "font-size":'30px',
                    "color":'hotpink'
                }
            },
            children:"hellow"
        }

vnode优点:

  • 可以跨平台 ,vNode以js对象作为基础,不依赖真实的环境,所以具有跨平台性
  • 虚拟DOM不一定可以提高性能,复杂试图下可以提升渲染性能

vdom 虚拟dom: 如果不是只有一个div元素呢,而是有一堆元素,那么此时会形成一个虚拟dom树,
vue通过建立虚拟dom来保持对真实dom的追踪变化
举个栗子:
如果要在一个数组中插入某一个元素,数组a b c d 中在b和c的中间插入一个e元素,vue对有key和无key时的区别
image.png
解读:首先对于列表中 abcd是没有变化的,在操作真的dom中,我们只需要插入一个e的li元素
那么vue中对于列表的更新到底是如果操作的呢?
其实vue会根据列表中有key无key调用不同的方法
有key就调用patchKeyedChildren 无key就调用patchUnkeyedChildren方法 先看无key的情况:
事实上c和d其实不需要任何改变,但是因为c被e使用了,导致后续的所有内容都要进行一次改动,并且最后进行新增 image.png

无key的源码是这样: image.png 解说:

1.首先拿取旧节点的数组长度和薪节点数组长度进行比较,取值最小的长度

为什么取最小的长度呢?如果取大的长度的length,那么小数组长度去遍历时会发生数组越界,这个很好理解把

    c2 = c2 || EMPTY_ARR
    const oldLength = c1.length
    const newLength = c2.length
    const commonLength = Math.min(oldLength, newLength)
    let i
    for (i = 0; i < commonLength; i++) {
      const nextChild = (c2[i] = optimized
        ? cloneIfMounted(c2[i] as VNode)
        : normalizeVNode(c2[i]))
      patch( //patch可以理解为更新
        c1[i],
        nextChild,
        container,
        null,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds,
        optimized
      )
    }

2.比较新旧两个节点的长度,如果旧节点长度>新节点长度,那么就删除多余的节点

    if (oldLength > newLength) {
      // remove old
      unmountChildren(
        c1,
        parentComponent,
        parentSuspense,
        true,
        false,
        commonLength
      )
    }

如果旧节点长度 < 新节点长度,那么就新增节点

else {
      // mount new
      mountChildren(
        c2,
        container,
        anchor,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds,
        optimized,
        commonLength
      )
    }

diff算法:比较新旧vnode的过程就是diff算法