vue中dom-diff算法

虚拟dom的概念
可以把Virtual DOM 理解为一个简单的JS对象,并且最少包含标签名( tag)、属性(attrs)和子元素对象( children)三个属性。不同的框架对这三个属性的命名会有点差别。总而言之,虚拟dom就是用js对象来描述dom节点

单纯的使用虚拟dom并不能优化性能,因为diff算法需要进行大量的比较。虽然减少了dom操作和渲染,但是大大增加了比较时运算的cup的计算负担。所以,虚拟dom配合着优化之后的diff算法,才能最大程度上的优化渲染。

1.4 diff算法
diff算法并不是虚拟dom独创的,他是被广泛应用在各个地方的。传统的diff算法,时间复杂度为O(n的三次方)。时间复杂度为n的三次方的算法基本是不可用的。
1遍历tree1
2遍历tree2
3排序
这三种操作都是循环遍历的,所以时间为n的三次方,这种算法不可用。所以React工程师们优化这个算法。给diff算法增加了几条规则
1只进行同级比较,不跨级比较
在这里插入图片描述

2当标签的类型(tag,例如div和span是两个类型的标签)不相同,则直接删除重新加载渲染,不在深度比较。
3当tag和key(key,和for循环中的key是相同的,key作为每一个标签的唯一标识,可以优化diff算法),两者相同的时候,则认为是相同的,不在进行深入的比较。

Diff算法中的patch
在这里插入图片描述

Dom diff算法中patch算法对节点的操作有创建节点,删除节点,更新节点

创建操作
在这里插入图片描述

其中,插入操作是把新增的节点插入到未处理节点之前,这样以来逻辑才正确。后面不管有多少个新增的节点,每一个都插入到所有未处理节点之前,位置才不会错。所以,合适的位置是所有未处理节点之前,而并非所有已处理节点之后。

删除操作
如果把newChildren里面的每一个子节点都循环一遍,能在oldChildren数组里找到的就处理它,找不到的就新增,直到把newChildren里面所有子节点都过一遍后,发现在oldChildren还存在未处理的子节点,那就说明这些未处理的子节点是需要被废弃的,那么就将这些节点删除。
更新操作
如果newChildren里面的某个子节点在oldChildren里找到了与之相同的子节点,并且所处的位置也相同,那么就更新oldChildren里该节点,使之与newChildren里的该节点相同。
其中更新节点中也包含创建子节点,更新子节点,移动子节点和删除子节点
移动操作
在这里插入图片描述

基于newVNode的基础之上,对oldVNode进行修改,插入的地方永远在未处理的节点之前。
Vue在更新子节点时是外层循环newChildren数组,内层循环oldChildren数组,把newChildren数组里的每一个元素分别与oldChildren数组里的每一个元素匹配,根据不同情况作出创建子节点、删除子节点、更新子节点以及移动子节点的操作。这样的双重遍历循环,会大大的增加时间复杂度。所以,vue对这个进行了优化。
优化策略
先把newChildren数组里的所有未处理子节点的第一个子节点和oldChildren数组里所有未处理子节点的第一个子节点做比对,如果相同,那就直接进入更新节点的操作;
如果不同,再把newChildren数组里所有未处理子节点的最后一个子节点和oldChildren数组里所有未处理子节点的最后一个子节点做比对,如果相同,那就直接进入更新节点的操作;
如果不同,再把newChildren数组里所有未处理子节点的最后一个子节点和oldChildren数组里所有未处理子节点的第一个子节点做比对,如果相同,那就直接进入更新节点的操作,更新完后再将oldChildren数组里的该节点移动到与newChildren数组里节点相同的位置;
如果不同,再把newChildren数组里所有未处理子节点的第一个子节点和oldChildren数组里所有未处理子节点的最后一个子节点做比对,如果相同,那就直接进入更新节点的操作,更新完后再将oldChildren数组里的该节点移动到与newChildren数组里节点相同的位置;
最后四种情况都试完如果还不同,那就按照之前循环的方式来查找节点。
如果在循环中,oldStartIdx大于oldEndIdx了,那就表示oldChildren比newChildren先循环完毕,那么newChildren里面剩余的节点都是需要新增的节点,把[newStartIdx, newEndIdx]之间的所有节点都插入到DOM中。
如果在循环中,newStartIdx大于newEndIdx了,那就表示newChildren比oldChildren先循环完毕,那么oldChildren里面剩余的节点都是需要删除的节点,把[oldStartIdx, oldEndIdx]之间的所有节点都删除。
以上是四个比较,按顺序分别是新前和旧前,新后和旧后,新前和旧后,新后和旧前进行四次比较,如果都没有再进行循环比较。
那就是在我们前面所说的优化策略中,节点有可能是从前面对比,也有可能是从后面对比,对比成功就会进行更新处理,也就是说我们有可能处理第一个,也有可能处理最后一个,那么我们在循环的时候就不能简单从前往后或从后往前循环,而是要从两边向中间循环。

在这里插入图片描述

在循环的时候,每处理一个节点,就将下标向图中箭头所指的方向移动一个位置,开始位置所表示的节点被处理后,就向后移动一个位置;结束位置所表示的节点被处理后,就向前移动一个位置;由于我们的优化策略都是新旧节点两两更新的,所以一次更新将会移动两个节点。说的再直白一点就是:newStartIdx和oldStartIdx只能往后移动(只会加),newEndIdx和oldEndIdx只能往前移动(只会减)。

所以,制定上面的规则,对diff算法进行了优化,把时间复杂度降低到了O(n)。大大的降低了diff算法的时间复杂度和渲染的性能。Diff算法和虚拟dom进行配合,大大的减少了渲染的开销。

对看源码中文社区后的一些心得体会
原文请关注vue源码中文社区

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值