快速diff
1、去掉相同的前置元素 j
2、去掉相同的后置元素 newEnd oldEnd
3、如果 j <= newEnd && j > oldEnd 需要挂载元素 j 到newEnd的元素, 挂载点为 newChildren[newEnd + 1]
4、如果 j <= oldEnd && j > newEnd 需要删除元素 j 到oldEnd的元素
5、如果 j <= newEnd && j <= oldEnd
5.1、建立数组source, 用于存放新vnode在旧vnode中位置
5.2、遍历新的vnode数组, 从j 到 newEnd 建立映射表 keyIndex = { key值 : 序号值}
5.3、遍历旧的vnode数组, 通过keyIndex确定旧节点是否存在新结点中, 并且回填数组source
-
不存在则卸载节点
-
如果存在
-
统计已经更新的节点的数量patched, 如果patched大于新节点数量newEnd - j + 1, 剩余节点直接卸载
-
patch更新节点
-
**用pos记录旧节点在新节点的位置的最大值, 如果没有值小于pos, 则说明没有移动, 不需要移动,更新pos, 如有值大于pos, 则设置moved为true **
-
moved为true时候, 获取最长递增子序列seq(记录newVnode最长递增的序号)
-
倒叙遍历newVnode中没有被前面步骤1、2去掉的元素,遍历序号为i
-
如果source[i]为 - 1, 则新建节点, 挂载点为当前节点下面一个节点,
-
如果遍历的序号i, 在最长递增子序列seq能够找到, 继续遍历
-
如果不能找到, 则插入节点, 挂载点为当前节点下面一个节点
-
-
注意: 为什么最后的挂载点为当前节点下面一个节点, 因为是倒叙遍历, 下面那个节点一定是已经插入, 或者当前节点就是第一个插入的节点
const newChildren = n2.children;
const oldChildren = n1.children;
let j = 0;
let oldVnode = oldChildren[j];
let newVnode = newChildren[j]
while (oldVnode.key === newVnode.key) {
patch(oldVnode, newVnode, container);
j++;
oldVnode = oldChildren[j];
newVnode = newChildren[j];
}
let oldEnd = oldChildren.length - 1;
let newEnd = newChildren.length - 1;
oldVnode = oldChildren[oldEnd];
newVnode = newChildren[newEnd];
while (oldVnode.key === newVnode.key) {
patch(oldVnode, newVnode, container);
oldVnode = oldChildren[--oldEnd];
newVnode = newChildren[--newEnd];
}
if(j > oldEnd && j <= newEnd) {
const anchorIndex = newEnd + 1;
const anchor = anchorIndex < newChildren.length ? newChildren[anchorIndex].el : null;
while (j <= newEnd) {
patch(null, newChildren[j++], container, anchor)
}
} else if(j > newEnd && j <= oldEnd) {
while (j <= oldEnd) {
unmount(oldChildren[j++])
}
} else {
const count = newEnd - j + 1;
const source = new Array(count)
source.fill(-1);
const oldStart = j;
const newStart = j;
const keyIndex = {};
let moved = false;
let pos = 0;
let patched = 0;
for(let i = newStart; i <= newEnd; i++) {
keyIndex[newChildren[i].key] = i;
}
for(let i = oldStart; i <= oldEnd; i++) {
oldVnode = oldChildren[i];
if(patched >= count) {
unmount(oldVnode);
continue;
}
const k = keyIndex[oldVnode.key];
if(typeof k !== "undefined") {
newVnode = newChildren[k];
patch(oldVnode, newVnode, container);
source[k - newStart] = i;
patched++;
if(k < pos) {
moved = true;
} else {
pos = k;
}
} else {
unmount(oldVnode);
}
}
let seq = lengthOfLIS(source); // 通过lis获取的结果是最长递增子序列的元素在source数组中位置索引, 如[2, 3, 1, -1]返回的是[0, 1];
let s = seq.length - 1;
for(let i = count - 1; i >= 0 ; i++) {
let pos = newStart + i;
let newVnode = newChildren[pos]
let nextPos = pos + 1;
const anchor = nextPos < newChildren.length ? newChildren[nextPos].el : null;
if(source[i] === - 1) {
patch(null, newVnode, container, anchor)
} else if(moved) {
if(seq[s] !== i) {
insert(newVnode.el, container, anchor)
} else {
s--;
}
}
}
}
// 下面是自己定义的lis的实现方法
function getPos(result, num) {
let left = 0;
let right = result.length;
while (left < right) {
let mid = left + ((right - left) >> 1)
if(num < result[mid]) {
right = mid;
} else if(num > result[mid]) {
left = mid + 1;
} else {
return mid;
}
}
return left;
}
var lengthOfLIS = function(nums) {
let result = [];
let list = [];
for(let i = 0 ; i < nums.length; i++) {
let num = nums[i];
if(num !== -1) {
let pos = getPos(result, num);
if(pos < result.length) {
result.splice(pos,1, num)
} else {
list.push(i);
result.push(num)
}
}
}
return list
};
console.log(lengthOfLIS([2, 3, 1, -1]))