// c1表示旧的子节点,c2表示新的子节点
const patchKeyedChildren = (c1, c2) => {
let i = 0
let e1 = c1.length - 1
let e2 = c2.length - 1
// 从前面比
while (i <= e1 && i <= e2) {
const n1 = c1[i]
const n2 = c2[i]
// 标签和key是否相同
// if (n1.type === n2.type && n1.key === n2.key)
if (n1 === n2) {
// 继续对比其属性和子节点
} else {
break
}
i++
}
// 从后面比
while (i <= e1 && i <= e2) {
const n1 = c1[e1]
const n2 = c2[e2]
// 标签和key是否相同
// if (n1.type === n2.type && n1.key === n2.key)
if (n1 === n2) {
// 继续对比其属性和子节点
} else {
break
}
e1--
e2--
}
console.log(i, e1, e2)
}
// 调用示例
patchKeyedChildren(['a', 'b', 'c', 'd'], ['a', 'b', 'c'])
通过这个函数可以得到:
1.
旧的:a b c 新的:a b c d
i = 3 e1 = 2 e2 = 3
2.
旧的: a b c 新的:d a b c
i = 0 e1 = -1 e2 = 0
3.
旧的:a b c d 新的:a b c
i = 3 e1 = 3 e2 = 2
4.
旧的:d a b c 新的: a b c
i = 0 e1 = 0 e2 = -1
5.
旧的:a b c d e i f g 新的:a b e c d h f g
i = 2 e1 = 5 e2 = 5
扩展:
旧的:a b c 新的:a b c d e f i = 3 e1 = 2 e2 = 5
旧的:a b c 新的:a b c i = 3 e1 = 2 e2 = 2
旧的:e d a b c 新的: a b c i = 0 e1 = 1 e2 = -1
旧的:c d e 新的:e c d h i = 0 e1 = 2 e2 = 3
从上面结果中我们可以找到规律:
当i大于e1时,只需添加新的子节点
当i大于e2时,只需删除旧的子节点
// 当i大于e1时
if (i > e1) {
if (i <= e2) {
while (i <= e2) {
const nextPos = e2 + 1
const anchor = nextPos < c2.length ? c2[nextPos].el : null
// 添加新的子节点c2[i]在anchor的前面
// todo
i++
}
}
}
// 当i大于e2时
else if (i > e2) {
if (i <= e1) {
while (i <= e1) {
// 删除旧的子节点c1[i]
// todo
i++
}
}
}
其它,特殊处理
// 其它
let s1 = i
let s2 = i
// 以新的子节点作为参照物
const keyToNewIndexMap = new Map()
for (let i = s2; i <= e2; i++) {
// 节点的key做为唯一值
// keyToNewIndexMap.set(c2[i].key, i)
keyToNewIndexMap.set(c2[i], i)
}
// 新的总个数
const toBePatched = e2 - s2 + 1
// 记录新子节点在旧子节点中的索引
const newIndexToOldIndexMap = new Array(toBePatched).fill(0)
// 循环老的子节点
for (let i = s1; i <= e1; i++) {
const oldChild = c1[i]
// let newIndex = keyToNewIndexMap.get(oldChild.key)
let newIndex = keyToNewIndexMap.get(oldChild)
// 在新子节点中不存在
if (newIndex === undefined) {
// 删除oldChild
// todo
} else {
newIndexToOldIndexMap[newIndex - s2] = i + 1 // 永远不会等于0, 这样0就可以表示需要创建了
// 继续对比其属性和子节点
// todo
}
}
console.log(newIndexToOldIndexMap)
// 需要移动位置
for (let i = toBePatched - 1; i >= 0; i--) {
let index = i + s2
let current = c2[index]
let anchor = index + 1 < c2.length ? c2[index + 1].el : null
if (newIndexToOldIndexMap[i] === 0) {
// 在anchor前面插入新的节点current
// todo
} else {
// 在anchor前面插入对应旧节点.el,current.el元素等于对应的旧节点.el(在其它代码中赋值了)
// todo
}
}
第1种和第2种比较简单,不做过多的讲解,我们来看看第3种,以下面为例
序号: 0 1 2 3 4 5 6 7 旧的:(a b) c d e i (f g) 新的:(a b) e c d h (f g)
// c1表示旧的子节点,c2表示新的子节点
const patchKeyedChildren = (c1, c2) => {
let i = 0
let e1 = c1.length - 1
let e2 = c2.length - 1
// 从前面比
while (i <= e1 && i <= e2) {
const n1 = c1[i]
const n2 = c2[i]
// 标签和key是否相同
// if (n1.type === n2.type && n1.key === n2.key)
if (n1 === n2) {
// 继续对比其属性和子节点
} else {
break
}
i++
}
// 从后面比
while (i <= e1 && i <= e2) {
const n1 = c1[e1]
const n2 = c2[e2]
// 标签和key是否相同
// if (n1.type === n2.type && n1.key === n2.key)
if (n1 === n2) {
// 继续对比其属性和子节点
} else {
break
}
e1--
e2--
}
console.log(i, e1, e2)
}
// 调用示例
patchKeyedChildren(['a', 'b', 'c', 'd'], ['a', 'b', 'c'])
通过这个函数可以得到:
1.
旧的:a b c 新的:a b c d
i = 3 e1 = 2 e2 = 3
2.
旧的: a b c 新的:d a b c
i = 0 e1 = -1 e2 = 0
3.
旧的:a b c d 新的:a b c
i = 3 e1 = 3 e2 = 2
4.
旧的:d a b c 新的: a b c
i = 0 e1 = 0 e2 = -1
5.
旧的:a b c d e i f g 新的:a b e c d h f g
i = 2 e1 = 5 e2 = 5
扩展:
旧的:a b c 新的:a b c d e f i = 3 e1 = 2 e2 = 5
旧的:a b c 新的:a b c i = 3 e1 = 2 e2 = 2
旧的:e d a b c 新的: a b c i = 0 e1 = 1 e2 = -1
旧的:c d e 新的:e c d h i = 0 e1 = 2 e2 = 3
从上面结果中我们可以找到规律:
当i大于e1时,只需添加新的子节点
当i大于e2时,只需删除旧的子节点
// 当i大于e1时
if (i > e1) {
if (i <= e2) {
while (i <= e2) {
const nextPos = e2 + 1
const anchor = nextPos < c2.length ? c2[nextPos].el : null
// 添加新的子节点c2[i]在anchor的前面
// todo
i++
}
}
}
// 当i大于e2时
else if (i > e2) {
if (i <= e1) {
while (i <= e1) {
// 删除旧的子节点c1[i]
// todo
i++
}
}
}
其它,特殊处理
// 其它
let s1 = i
let s2 = i
// 以新的子节点作为参照物
const keyToNewIndexMap = new Map()
for (let i = s2; i <= e2; i++) {
// 节点的key做为唯一值
// keyToNewIndexMap.set(c2[i].key, i)
keyToNewIndexMap.set(c2[i], i)
}
// 新的总个数
const toBePatched = e2 - s2 + 1
// 记录新子节点在旧子节点中的索引
const newIndexToOldIndexMap = new Array(toBePatched).fill(0)
// 循环老的子节点
for (let i = s1; i <= e1; i++) {
const oldChild = c1[i]
// let newIndex = keyToNewIndexMap.get(oldChild.key)
let newIndex = keyToNewIndexMap.get(oldChild)
// 在新子节点中不存在
if (newIndex === undefined) {
// 删除oldChild
// todo
} else {
newIndexToOldIndexMap[newIndex - s2] = i + 1 // 永远不会等于0, 这样0就可以表示需要创建了
// 继续对比其属性和子节点
// todo
}
}
console.log(newIndexToOldIndexMap)
// 需要移动位置
for (let i = toBePatched - 1; i >= 0; i--) {
let index = i + s2
let current = c2[index]
let anchor = index + 1 < c2.length ? c2[index + 1].el : null
if (newIndexToOldIndexMap[i] === 0) {
// 在anchor前面插入新的节点current
// todo
} else {
// 在anchor前面插入对应旧节点.el,current.el元素等于对应的旧节点.el(在其它代码中赋值了)
// todo
}
}
第1种和第2种比较简单,不做过多的讲解,我们来看看第3种,以下面为例
序号: 0 1 2 3 4 5 6 7 旧的:(a b) c d e i (f g) 新的:(a b) e c d h (f g)