Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
目的
找到可复用节点,然后移动到该去的位置。
参数:
- n1 旧VNode
- n2 新VNode
- container 挂载点
思路
- 新节点组遍历旧节点组,如果旧节点的索引 <
lastIndex(最大索引),将其移动到最后,反之更新lastIndex - 若在旧节点组中没有找到新节点,则在上一个节点之后插入
- 遍历旧节点组,查找是否有不存在新节点组中的节点,卸载
流程图
- 遍历新节点 创建
lastIndex - 在旧的节点中找到 P3
lastIndex赋值为旧节点中 P3 的index
- 在旧的找到 新的P2
key=== 旧的P2key - 旧的P2 的 索引 <
lastIndex移动 P2
- 在旧的中找到 新的P1
key=== 旧的P1key - 旧的P1 的 索引 <
lastIndex移动 P1
简单diff算法步骤
oldChildren定义为旧节点n1的childrennewChildren定义为新节点n2的children- 定义
lastIndex为0 ,在oldChildren中找到key相同的最大索引值 - 循环
newChildren,定义i为当前索引- 定义
newVNode为当前节点 - 定义
find为是否在oldChildren中找到相同key节点 - 循环
oldChildren,定义 j 是当前索引- 定义
oldVNode为当前节点 patch( n1 , n2 , container )- j < lastIndex ?
- 需要移动元素,找到
newVNode的下一个节点prevVNode?- 有->
- 定义
anchor为prevVNode对应真实DOM的下一个DOM insert ( newVNode.e l,container,anchor )
- 定义
- 无-> 说明是第一个元素,无需操作
- 有->
- 需要移动元素,找到
lastIndex赋值为j- 结束该循环
- 定义
- find 为假?
- 定义
anchor为null(要插入的位置) - 找到
newVNode的上一个节点( oldChildren[ i -1 ] ), 作为prevVNode?- 有->
anchor为prevVNode对应真实DOM的下一个DOM - 无->
anchor作为container的firstChild
- 有->
patch(null,newVNode,container,anchor)挂载新的节点
- 定义
- 定义
- 循环
oldChildren- 定义
oldVNode - 定义
has为newChildren中是否有当前oldVNode的key has假,说明newChildren中没有该节点- 卸载该节点
- 定义
总结
key相当于虚拟节点的身份证,通过比较key来找到可复用的节点- 简单diff算法的核心逻辑:拿新的节点去在旧的节点中找可复用的节点,找到了则记录改节点位置的索引(最大索引)。在此过程中,如果旧节点的索引小于最大索引,说明该节点对应的真实DOM需要移动
- 添加节点:如果没有在旧节点中找到,则在该新节点的上一个DOM的后一个兄弟DOM前添加
- 删除节点:在遍历完成后,再次遍历旧节点,如果没有在新节点中找到对应节点,则卸载该节点。
可以看到这种方式不是最优的解法 所以还有 双端diff算法