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 有key时diff算法排序逻辑分为五步如下:
* 前序算法前面元素与前面元素比较如 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

浙公网安备 33010602011771号