diff算法

1.diff算法是什么

diff算法是一种通过同层的树节点进行比较的高效算法,探讨的是虚拟DOM树发生变化后,生成DOM树更新补丁的方式。对比新旧两株虚拟DOM树的差异,将更新补丁作用于真实DOM,以最小成本完成视图更新。

有两个特点:

  • 比较只会在同层比较,不会跨层比较。

  • 在diff比较的过程中,循环从两边向中间比较。

2.比较方式

整体策略为:深度优先,同层比较。

(1)比较只会发生在同层级进行比较,不会跨层级比较。

(2)比较过程中,循环从两边向中间靠拢:

old节点:A->B->C>D

new节点:D->C->E->A->B->F

最开始oldStartIndex=A, oldEndIndex=D;newStartIndex=D, newEndIndex=F

第一次从newStartIndex比较,从oldStartIndex到oldEndIndex循环,发现旧节点D与新节点D相同,直接复用旧节点D作为diff后的第一个真实节点,同时旧节点endIndex移到C的位置,新节点newStartIndex移动到C的位置。

第二次循环从oldStartIndex(A)到oldEndIndex(C)查找新节点newStartIndex(C),发现旧节点C与新节点C相同,同样diff后将创建C的真实节点插入到第一次创建的D节点后面。同时旧节点的endIndex移动到B,新节点的newStartIndex移动到E。

第三次循环从oldStartIndex(A)到oldEndIndex(B)查找新节点newStartIndex(E),发现E没有找到,此时只能创建新的真实节点E,插入到第二次创建的C节点之后,同时新节点的newStartIndex移动到A。

第四次循环从oldStartIndex(A)到oldEndIndex(B)查找新节点newStartIndex(A),于是diff后创建了A的真实节点,插入到前一次创建的E节点后。同时旧节点oldStartIndex移动到B,新节点的newStartIndex移动到B。

第五次循环和第四次循环一样,因此diff后创建了B真实节点插入到前一次创建的A节点后面。同时旧节点的oldStartIndex移动到C,oldEndIndex移动到A,新节点的newStartIndex移动到F。

旧节点的oldStartIndex已经大于oldEndIndex了,需要创建newStartIndex和newEndIndex之间的所有节点,也就是F,直接创建F节点对应的真实节点到B节点后面。

3.原理

当数据发生改变时,set方法会调用Dep.notify通知所有订阅者watcher,订阅者就会调用patch给真实的DOM打补丁,更新相应的视图。

patch(oldVnode, vnode, hydrating, removeOnly)
说明:oldVnode旧节点,vnode新节点。
函数内主要有如下逻辑:
(1)没有新节点,直接触发旧节点的destroy钩子。
(2)没有旧节点,说明是页面刚开始初始化的时候,此时根本不需要比较了,直接全是新建,所以只调用createElm。
(3)旧节点和新节点自身一样,通过sameVnode判断节点是否一样,一样时,直接调用patchVnode去处理这两个节点。
(4)旧节点和新节点自身不一样,直接创建新节点,删除旧节点。
 
patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly)
主要做了如下判断:
(1)新节点是否是文本节点,如果是则直接更新dom的文本内容为新节点的文本内容。
(2)新节点和旧节点如果都有子节点,则处理比较更新子节点。
(3)只有新节点有子节点,旧节点没有,那么不用比较了,所有节点都是全新的,所以直接全部新建。即创建出所有新DOM并且添加父节点。
(4)只有旧节点有子节点而新节点没有,说明更新后的页面,旧节点全部都不见了,那么要做的就是把所有的旧节点删除,也就是把DOM直接删除。
 
子节点不完全一致,则会调用updateChildren:
(1)当新老VNode节点的start相同时,直接patchVnode,同时新老VNode节点的开始索引都加1.
(2)当新老VNode节点的end相同时,同样直接patchVnode,同时新老VNode节点的结束索引都减1.
(3)当老VNode节点的start和新VNode节点的end相同时,这时候在patchVnode后,还需要将当前真实DOM节点移动到oldEndVnode后面,同时老VNode节点开始索引加1,新VNode节点的结束索引减1.
(4)当老VNode节点的end和新VNode节点的start相同时,这时候在patchVnode后,还需要将当前真实dom节点移动到oldEndVnode的前面,同时老Vnode节点结束索引减1,新Vnode节点的开始索引加1.
(5)如果都不满足以上4种情况,说明没有相同的子节点可以复用,则又分为两种情况:
从旧的VNode为key值,对应index序列为value值的哈希表种找到与newStartVnode一致key的旧的VNode节点,再进行patchVnode,同时将这个真实DOM移动到oldStartVnode对应的真实dom的前面。
调用createElm创建一个新的dom节点放到当前newStartIndex的位置。

小结:

当数据发生改变时,订阅者watcher会调用patch给真实的DOM打补丁:

通过isSameVnode进行判断,如果相同则调用patchVnode方法

patchVnode做了如下操作

找到对应的真实DOM,称为el

如果都有文本节点且不相等,将el文本节点设置为Vnode的文本节点

如果oldVnode有子节点而VNode没有,则删除el子节点

如果oldVnode没有子节点而VNode有,则将VNode的子节点真实化后添加到el

如果两者都有子节点,则执行updateChildren函数比较子节点

updateChildren主要做了以下操作:

设置新旧VNode的头尾指针

新旧头尾指针进行比较,循环向中间靠拢,根据情况调用patchVnode进行patch重复流程、调用createElem创建一个新节点,从哈希表寻找一个key一致的VNode节点再分情况操作。

  • 33
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前端的diff算法在虚拟DOM的实现中起着重要的作用,它用于比较两个虚拟DOM树的差异,然后只对差异部分进行更新,以提高性能和效率。在面试中,可能会问到一些与前端diff算法相关的问题。以下是一些常见的问题和答案供参考: 1. 什么是前端diff算法? 前端diff算法是指用于比较两个虚拟DOM树之间差异的算法。通过比较新旧虚拟DOM树的差异,可以确定需要更新的部分,从而减少不必要的页面重绘和重新渲染,提高性能和效率。 2. 常见的前端diff算法有哪些? 常见的前端diff算法包括: - O(n²)算法:遍历新旧节点进行比较,时间复杂度为O(n²),性能较差。 - O(n)算法:采用双指针或者哈希表等方式,将遍历时间复杂度优化为O(n),例如React中采用的Virtual DOM diff算法。 - Fiber算法:React Fiber算法是一种增量渲染算法,通过将更新操作拆分为多个单元,可以在每个帧中执行一部分工作,从而提高用户体验。 3. React中采用的前端diff算法是什么? React中采用的是一种基于O(n)算法的Virtual DOM diff算法。该算法通过遍历新旧虚拟DOM树的节点,对比差异并更新只有差异的部分,以提高性能。 4. 前端diff算法的优化策略有哪些? 前端diff算法可以通过以下优化策略提高效率: - 对比时忽略静态节点:对比时可以忽略没有变化的静态节点,减少不必要的对比操作。 - 使用唯一标识符:给每个节点添加唯一标识符,可以更精确地确定哪些节点需要更新。 - 列表元素的优化:在对比列表元素时,可以使用Key属性标识唯一性,以减少重新排序和重渲染的开销。 这些问题涵盖了前端diff算法的概念、常见算法以及优化策略。在面试中,你可以根据自己的理解和经验进行回答。希望对你有帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值