简单diff算法
新节点[] 旧节点[]
循环遍历新节点数组,比对vnode的type和key属性,如果相同则可以复用,复用方式为将旧节点oldVNode.el赋值给新节点newVNode.el,同时保存旧节点处于旧列表中的位置作为lastIndex,后续继续进行匹配,
如果匹配到了且小于lastIndex则需要移动到lastIndex对应节点的后面,
如果大于则直接更新lastIndex,
如果没有匹配到则表示无法复用,需要创建节点。
遍历完成后,再遍历旧节点数组,查找新节点中有没有对应key值,没有则表示该节点被删除。
很明显,对于下面这种情况简单diff算法将会进行两次真实DOM节点的移动,仍然存在优化空间
n1 -> n2 -> n3 To n3 -> n1 -> n2
双端diff算法
while(newStartIdx <= newEndIdx && oldStartIdx <= oldEndIdx){
//进行双端比较
...
}
if (oldEndIdx < oldStartIdx && newStartIdx <= newEndIdx) {
//添加新节点
...
} else if (newEndIdx < newStartIdx && oldStartIdx <= oldEndIdx){
// 移除操作
...
}
快速diff算法
加个一步预处理
从前往后while循环,一一对比key值,如果相同则打补丁,然后继续往后遍历,直到key值不相等,这里用j来记录index
从后往前while循环,一一对比key值,如果相同则打补丁,然后继续往前遍历,直到key值不相等,这里分别记录oldEnd和newEnd
//旧节点遍历完了,新节点还没有遍历完
if(j > oldEnd && j <= newEnd){
//新增一系列节点
while(j <= newEnd){
...
}
}
//新节点遍历完了,旧节点还没有遍历完
else if(j > newEnd && j <= oldEnd){
//删除一系列节点
while(j <= oldEnd){
...
}
}
else{
//对剩余不符合双端匹配的节点进行处理
//生成一个长度为newEnd - j + 1长度的source数组,存储新节点在旧节点中的位置索引,然后计算出一个最长递增子序列,辅助完成DOM移动操作
//遍历旧节点数组,匹配新节点中key值对应的节点(这里可以建立索引匹配将复杂度从O(n^2)降到O(n)),进行打补丁操作,更新source数组
//同时判断是否移动逻辑同简单diff算法,存储最大索引pos,小于则表示需要移动,大于则更新pos
}
上图表示索引0,1不需要移动
接下来,处理剩余索引的移动情况
如果source数组中存的索引值为-1,表示需要挂载 对应p-7
判断索引2是否等于source数组的值1,不等表示需要移动 对应p-2
如果索引1与source数组对应的值1相等,则表示不需要移动,继续往下遍历 对应p-4
...
遍历结束