vue2 diff
前置
- 节点
- 旧列表的第一个节点表示为
oldStartNode
- 旧列表的最后一个节点表示为
oldEndNode
- 新列表的第一个节点表示为
newStartNode
- 新列表的最后一个节点表示为
newEndNode
- 下标
- 旧列表的头指针表示为
oldStartIndex
- 旧列表的尾指针表示为
oldEndIndex
- 新列表的头指针表示为
newStartIndex
- 新列表的尾指针表示为
newEndIndex
第一轮
- 使用
oldStartNode与newStartNode对比
- 相同,
oldStartIndex 与 newStartIndex,同时向后移动一位,元素无需移动
- 使用
oldEndNode与newEndNode对比
- 相同,
oldEndIndex 与 newEndIndex,同时向前移动一位,元素无需移动
- 使用
oldStartNode与newEndNode对比
- 相同,
oldStartIndex向后移动一位,newEndIndex向前移动一位,oldStartNode移动到原本尾节点的后面
- 使用
oldEndNode与newStartNode对比
- 相同,
oldEndIndex向前移动一位newStartIndex向后移动一位,oldEndNode移动到原本头节点的前面
第二轮
- 当四次对比都没找到复用节点时,我们拿新列表的第一个节点去旧列表中找与其
key相同的节点
- 找到:节点移动
oldStartIndex之前,旧列表中的节点改为undefined,oldStartIndex后移
- 没找到:直接创建一个新的节点放到最前面就可以了,
newStartIndex后移
终止条件
- 当
oldStartIndex 大于 oldEndIndex,但是新列表中还有剩余的节点,我们只需要将剩余的节点依次插入到oldStartNode的DOM之前
- 当
newEndIndex小于newStartIndex时,我们将旧列表剩余的节点删除
vue3 diff
前置
- 节点
- 旧列表的第一个节点表示为
oldStartNode
- 旧列表的最后一个节点表示为
oldEndNode
- 新列表的第一个节点表示为
newStartNode
- 新列表的最后一个节点表示为
newEndNode
- 下标
- 旧列表的头指针表示为
oldStartIndex
- 旧列表的尾指针表示为
oldEndIndex
- 新列表的头指针表示为
newStartIndex
- 新列表的尾指针表示为
newEndIndex
第一轮
- 使用
oldStartNode与newStartNode对比
- 相同,
oldStartIndex 与 newStartIndex,同时向后移动一位,元素无需移动
- 不相同,停止
- 使用
oldEndNode与newEndNode对比
- 相同,
oldEndIndex 与 newEndIndex,同时向前移动一位,元素无需移动
- 不相同,停止
第二轮
- 这时会出现四种场景
- 旧列表遍历结束,新列表遍历结束:不需要第二轮遍历,对应 位置无变化
- 旧列表有剩余节点,新列表遍历结束:老节点全部删除
- 旧列表遍历结束,新列表有剩余节点:新节点全部插入
- 旧列表有剩余节点,新列表有剩余节点,进入diff
diff
- 以剩余的新节点为映射数组,并生成一个映射数组等长的下标数组,与遍历剩余的老节点,看老节点是否在映射数组中
- 存在:给映射数组对应下标数组写入老节点的下标
- 不存在:老节点需要删除
- 注意下标数组默认都是-1,-1代表新节点需要新增
- 在遍历时也会比较当前节点下标是否大于上一个节点
- 如果大于则不需要移动
- 如果小于则需要移动
- 下标数组使用最长递增子序列算法(下面会说)算出最长递增的数组
- 遍历下标数组,并在最长递增的数组中查找
- 存在:不需要移动
- 不存在:新建节点插入到前一个vnode节点之后
- 为什么是这样移动的呢?
- 首先列表是从头到尾遍历的。这就意味着对于当前VNode节点来说,该节点之前的所有节点都是排好序的,如果该节点需要移动,那么只需要将DOM节点移动到前一个vnode节点之后就可以
最长递增子序列算法
function getSequence(arr) {
const dp = new Array(arr.length).fill(1)
for (let i = 1; i < arr.length; i++) {
for (let j = i - 1; j >= 0; j--) {
if (arr[i] > arr[j]) dp[i] = Math.max(dp[i], dp[j] + 1)
}
}
let max = Math.max(...dp)
const result = []
for (let i = arr.length - 1; max > 0; i--) {
if (dp[i] == max) {
result.unshift(arr[i])
max--
}
}
return result
}
function getSequence(arr) {
const p = arr.slice()
const result = [0]
for (let i = 1; i < arr.length; i++) {
if (arr[i] > arr[result[result.length - 1]]) {
p[i] = result[result.length - 1]
result.push(i)
} else {
let l = 0
let r = result.length - 1
let m = Math.floor((l + r) / 2) | 0
while (l < r) {
if (arr[result[m]] < arr[i]) {
l = m + 1
} else {
r = m - 1
}
m = Math.floor((l + r) / 2)
}
result[l] = i
p[i] = result[l - 1]
}
}
let len = result.length
let max = result[len - 1]
while (len > 0) {
len--
result[len] = max
max = p[max]
}
return result
}