vue3 虚拟dom与diff算法(小满zs vue3 笔记五)

diff算法(虚拟dom会生成两个节点如c1,c2)

图1

vue3 diff算法原码地址:  https://github.com/vuejs/core

1. diff 算法主要是说renderer.ts中patchChildren这段代码逻辑,如下:

 

 2. diff算法排序分为无key时diff算法排序逻辑和有key时diff算法排序逻辑

 2.1 无key时diff算法排序逻辑, 分为三步如下,如图1中无key: 

  * 通过for循环patch重新渲染元素,来替换

  * 删除

      * 新增

const patchUnkeyedChildren = (
    c1: VNode[],
    c2: VNodeArrayChildren,
    container: RendererElement,
    anchor: RendererNode | null,
    parentComponent: ComponentInternalInstance | null,
    parentSuspense: SuspenseBoundary | null,
    isSVG: boolean,
    slotScopeIds: string[] | null,
    optimized: boolean
  ) => {
    c1 = c1 || EMPTY_ARR
    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]))
        //1. 循环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 {
      //3.  新增
      // mount new
      mountChildren(
        c2,
        container,
        anchor,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds,
        optimized,
        commonLength
      )
    }
  }

 2.2 keydiff算法排序逻辑分为五步如下:

  * 前序算法前面元素与前面元素比较如 isSameVNodeType,如果不一样,跳出循环

    * 尾序算法尾和尾比较,如果不一样,跳出循环

    * 新节点如果多出来就挂载(新增)

 * 旧节点如果多出来就卸载(删除) ,前4点如图1 有key

 * 乱序(涉及最长递增子系列算法),3个点

  *  构建新节点的映射关系

例子:        
    key 1 2 3 4 5
 index 0 1 2 3 4
// sort: 
   key 5 4 3 2 1
index 0 1 2 3 4
5 -> 0 4 -> 1 3 -> 2 2 -> 3 1 -> 4     
 * 记录新节点在旧节点的位置(newIndexToOldIndexMap方法),如果多余点删除(unmount),如果新节点不包含旧节点也删除,节点出现交叉,做移动标记,求最长递增算法
      // 5.2 loop through old children left to be patched and try to patch
      // matching nodes & remove nodes that are no longer present
      let j
      let patched = 0
      const toBePatched = e2 - s2 + 1
      let moved = false
      // used to track whether any node has moved
      let maxNewIndexSoFar = 0
      // works as Map<newIndex, oldIndex>
      // Note that oldIndex is offset by +1
      // and oldIndex = 0 is a special value indicating the new node has
      // no corresponding old node.
      // used for determining longest stable subsequence
      // 记录新节点在旧节点的位置数组
      const newIndexToOldIndexMap = new Array(toBePatched)
      for (i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0

      for (i = s1; i <= e1; i++) {
        const prevChild = c1[i]
        if (patched >= toBePatched) {
          // all new children have been patched so this can only be a removal
          // 如果多余节点删除
          unmount(prevChild, parentComponent, parentSuspense, true)
          continue
        }
        let newIndex
        if (prevChild.key != null) {
          newIndex = keyToNewIndexMap.get(prevChild.key)
        } else {
          // key-less node, try to locate a key-less node of the same type
          for (j = s2; j <= e2; j++) {
            if (
              newIndexToOldIndexMap[j - s2] === 0 &&
              isSameVNodeType(prevChild, c2[j] as VNode)
            ) {
              newIndex = j
              break
            }
          }
        }
        // 如果新节点不包含旧节点也删除
        if (newIndex === undefined) {
          unmount(prevChild, parentComponent, parentSuspense, true)
        } else {
          newIndexToOldIndexMap[newIndex - s2] = i + 1
          if (newIndex >= maxNewIndexSoFar) {
            maxNewIndexSoFar = newIndex
          } else {
            // 节点出现交叉,做移动标记,求最长递增算法
            moved = true
          }
          patch(
            prevChild,
            c2[newIndex] as VNode,
            container,
            null,
            parentComponent,
            parentSuspense,
            isSVG,
            slotScopeIds,
            optimized
          )
          patched++
        }
      }

      * 求最长递增(升序)算法排序(图1 最长递增算法)

最长递增算法: 1. 选默认递增序列如图1 乱序dp 1 1... 2. 比较 10 -> 1 , 9 -> 9比10小 1  ,2 -> 2比9,10小 1, 5 -> 5比9,10,2 比2大,所以1基础上加1 2,3 -> 比同上,比2大 2, 7 -> 比10..5..3比5大(5对应的是2,加1) 3, 101 -> 比10..7都大取最大值加1 4, 18 -> 比10..101 同上 4 3. 对就索引还是0 1 2 3....

代码事例: 

<template>
  <div>
    <div :key="item" v-for="(item) in Arr">{{ item }}</div>
  </div>
</template>
 
 
 
<script setup lang="ts">
const Arr: Array<string> = ['A', 'B', 'C', 'D']
Arr.splice(2,0,'DDD')
</script>
 
 
<style>
</style>

  

参考文章: https://xiaoman.blog.csdn.net/article/details/122778560?spm=1001.2014.3001.5502

posted @ 2023-07-06 16:20  TheYouth  阅读(89)  评论(0编辑  收藏  举报