diff分为5步:
- 自前向后的对比:会依次获取相同下标的oldChild和newChild
- 如果oldChild和newChild为相同的VNode,则直接通过patch进行补丁即可
- 如果oldChild和newChild为不相同的VNode,则会跳出循环
- 每次处理成功,则会自增i标记,表示:自前向后已处理过的节点数量
- 自后向前的对比
- 新节点多于旧节点,需要挂载
- 旧节点多于新节点,需要卸载
- 乱序(最长递增子序列)
最长递增子序列
定义:在一个给定的数值序列中,找到一个子序列,使这个子序列元素的数值依次递增,并且子序列的长度尽可能的大 作用:减少移动的次数,提升性能
动态规划----》解最值问题
备忘录:(DP Table)
解决方案---二分查找优化
const nums = [10,2,5,3,7,18]
function lengthOfLIS (nums:number[]):number {
if (nums.length <= 1) {
return nums;
}
let tail = [nums[0]];//存放最长上升子序列数组
for (let i = 0; i < nums.length; i++) {
if (nums[i] > tail[tail.length - 1]) {
tail.push(nums[i]);//当nums中的元素比tail中的最后一个大时 可以放心push进tail
} else {//否则进行二分查找
let left = 0;
let right = tail.length - 1;
while (left < right) {
let mid = (left + right) >> 1;//>>运算符执行有符号右移位运算
if (tail[mid] < nums[i]) {
left = mid + 1;
} else {
right = mid;
}
}
tail[left] = nums[i];//将nums[i]放置到合适的位置,此时前面的元素都比nums[i]小
}
}
return tail.length;
}
patchKeyedChildren方法究竟做了什么?
第一步的事情就是从头开始寻找相同的vnode,然后进行patch,如果发现不是相同的节点,那么立即跳出循环。
第二步从尾开始同前diff
如果老节点是否全部patch,新节点没有被patch完,创建新的vnode
如果新节点全部被patch,老节点有剩余,那么卸载所有老节点
最长稳定序列
总结
从头对比找到有相同的节点 patch ,发现不同,立即跳出。 如果第一步没有patch完,立即,从后往前开始patch ,如果发现不同立即跳出循环。 如果新节点大于旧节点数 ,对于剩下的节点全部以新的vnode处理( 这种情况说明已经patch完相同的vnode )。 对于旧节点大于新节点的情况 , 对于超出的节点全部卸载 ( 这种情况说明已经patch完相同的vnode )。 不确定的元素( 这种情况说明没有patch完相同的vnode ) 与 3 ,4对立关系。 1 把没有比较过的新的vnode节点,通过map保存 记录已经patch的新节点的数量 patched 没有经过 path 新的节点的数量 toBePatched 建立一个数组newIndexToOldIndexMap,每个子元素都是[ 0, 0, 0, 0, 0, 0, ] 里面的数字记录老节点的索引 ,数组索引就是新节点的索引。 开始遍历老节点 ① 如果 toBePatched新的节点数量为0 ,那么统一卸载老的节点 ② 如果,老节点的key存在 ,通过key找到对应的index ③ 如果,老节点的key不存在 1 遍历剩下的所有新节点 2 如果找到与当前老节点对应的新节点那么 ,将新节点的索引,赋值给newIndex ④ 没有找到与老节点对应的新节点,卸载当前老节点。 ⑤ 如果找到与老节点对应的新节点,把老节点的索引,记录在存放新节点的数组中, 1 如果节点发生移动 记录已经移动了 2 patch新老节点 找到新的节点进行patch节点 遍历结束