# 参数
- n1 - 旧的节点
- n2 - 新的节点
- container 挂载点
# 思路
- 循环遍历 比较
- 是否被处理过(6 中的非理想情况)。
- 新首-旧首,打补丁,新首节点索引+1,旧首节点索引+1。
- 新尾-旧尾,打补丁,新尾节点索引-1,旧尾节点索引-1。
- 新尾-旧首,打补丁,旧首对应的 DOM 移动到旧尾节点的后面,新尾节点索引-1,旧首节点索引+1。
- 新首-旧尾,打补丁,旧尾对应的 DOM 移动到旧首节点的前面,新首节点索引+1,旧尾节点索引-1。
- 考虑非理想情况 在旧节点组中找新首。找到则将 DOM 移动到最前,且在旧节点组中设为 undefined;没找到则添加新元素到最前。
- 如果有新节点剩下,遍历剩下的新节点,挂载到当前旧节点组的头部节点。
- 如果有旧节点剩下,遍历剩下的旧节点,卸载。
# 流程图
# 准备工作

# 循环开始



# 循环完成,检查新增或者卸载

# 实现步骤
定义
oldChildren为 n1 的children定义
newChildren为 n2 的children// 定义四个索引值定义
oldStartIndex为oldChildren的首位索引值定义
oldEndIndex为oldChildren的最后位索引值定义
newStartIndex为newChildren的首位索引值定义
newEndIndex为newChildren的最后位索引值 // 定义四个索引所对应的 VNode 节点定义
oldStartVNode为oldChildren[oldStartIndex]定义
oldEndVNode为oldChildren[oldEndIndex]定义
newStartVNode为newChildren[newStartIndex]定义
newEndVNode为newChildren[newEndIndex]// 进行比较循环?
newStartIndex <= newEndIndex且oldStartIndex <= oldEndIndexoldStartVNode 不存在的话? 说明该节点已经处理过了
- 将
oldStartVNode指向oldChildren[++oldStartIndex]
- 将
oldStartVNode.key === newStartVNode.key?- 调用
patch(oldStartVNode,newStartVNode,container)打补丁 - 将
oldStartVNode指向oldChildren[++oldStartIndex] - 将
newStartVNode指向newChildren[++newStartIndex]
- 调用
oldEndVNode.key === newEndVNode.key?- 调用
patch(oldStartVNode, newStartVNode, container)打补丁 - 将
oldEndVNode指向oldChildren[--oldEndIndex] - 将
newStartVNode指向newChildren [--newStartIndex]
- 调用
oldStartVNode.key === newEndVNode.key?- 调用
patch(oldStartVNode, newEndVNode, container )打补丁 - 调用
insert(oldStartVNode.el ,container ,oldEndVNode.el.nextSiling )将oldStartVNode移动到oldEndVNode的下一个节点 前 - 将
oldStartVNode指向oldChildren [ ++oldStartIndex ] - 将
newEndVNode指向newChildren[ --newEndIndex ]
- 调用
oldEndVNode.key === newStartVNode.key?- 调用
patch( oldEndVNode, newStartVNode , container )打补丁 - 调用
insert ( oldEndVNode.el , container , oldStartVNode.el )将oldEndVNode移动到oldStartVNode前 - 将
oldEndVNode指向oldChildren [ --oldEndIndex ] - 将
newStartVNode指向newChildren[ ++newStartIndex ]
- 调用
以上都不满足
- 定义
idxInOld, 遍历oldChildren,找是否有和newStartVNode相同的key? idxInOld > 0 在 oldChildren中找到和newStartVNode相同的key- 定义
vnodeToMove为oldChildren[idxInOld] - 调用
patch( vnodeToMove, newStartVNode , container )打补丁 - 调用
insert ( vnodeToMove.el , container , oldStartVNode.el )将vnodeToMove移动到oldStartVNode前 - 将
oldChildren[idxInOld]设置为undefined
- 定义
idxInOld < 0没有找到- 调用
patch(null,newStartVNode,container,oldStartVNode.el), 添加newStartVNode - 将
newStartVNode指向newChildren[ ++newStartIndex ]
- 调用
- 定义
oldEndIndex < oldStartIndex && newEndIndex <= newStartIndex检查索引的情况,看是否有需要新增的节点- 循环剩下的新节点 然后调用
patch(null,newChildren[i],container,oldStartVNode.el)新增到 oldStartVNode 前
- 循环剩下的新节点 然后调用
newEndIndex < newStartIndex && oldEndIndex >= oldStartIndex检查是否有需要删除的节点- 循环剩下的旧节点,然后调用
unmount(oldChildren[i])
- 循环剩下的旧节点,然后调用
# 总结
双端diff算法指的就是在新旧两组节点的四个端点分别进行比较,并试图找到可用节点。相比较简单diff算法 (opens new window),同样的场景下,DOM移动次数更少。