Vue的Diff算法
Diff算法
新旧虚拟DOM对比的时候,Diff算法比较只会在同层级进行, 不会跨层级比较。 所以Diff算法是:深度优先算法。 时间复杂度:O(n)
如何触发
当数据改变时,会触发setter,并且通过Dep.notify去通知所有订阅者Watcher,订阅者们就会调用patch方法,给真实DOM打补丁,更新相应的视图。

核心方法:
1. patch方法
1 | |
这个方法作用就是,对比当前同层的虚拟节点是否为同一种类型的标签(同一类型的标准,下面会讲):
- 是:继续执行
patchVnode方法进行深层比对 - 否:没必要比对了,直接整个节点替换成
新虚拟节点
2. sameVnode方法
1 | |
patch关键的一步就是sameVnode方法判断是否为同一类型节点
3. patchVnode方法
1 | |
这个函数做了以下事情:
- 找到对应的
真实DOM,称为el - 判断
newVnode和oldVnode是否指向同一个对象,如果是,那么直接return - 如果他们都有文本节点并且不相等,那么将
el的文本节点设置为newVnode的文本节点。 - 如果
oldVnode有子节点而newVnode没有,则删除el的子节点 - 如果
oldVnode没有子节点而newVnode有,则将newVnode的子节点真实化之后添加到el - 如果两者都有子节点,则执行
updateChildren函数比较子节点,这一步很重要
4. updateChildren方法
这是patchVnode里最重要的一个方法,新旧虚拟节点的子节点对比,就是发生在updateChildren方法中
是怎么样一个对比方法呢?就是首尾指针法,新的子节点集合和旧的子节点集合,各有首尾两个指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14<ul>
<li>a</li>
<li>b</li>
<li>c</li>
</ul>
修改数据后
<ul>
<li>b</li>
<li>c</li>
<li>e</li>
<li>a</li>
</ul>那么新旧两个子节点集合以及其首尾指针为:
然后会进行互相进行比较,总共有五种比较情况:
- 1、
oldS 和 newS使用sameVnode方法进行比较,sameVnode(oldS, newS)- 2、
oldS 和 newE使用sameVnode方法进行比较,sameVnode(oldS, newE)- 3、
oldE 和 newS使用sameVnode方法进行比较,sameVnode(oldE, newS)- 4、
oldE 和 newE使用sameVnode方法进行比较,sameVnode(oldE, newE)- 5、如果以上逻辑都匹配不到,再把所有旧子节点的
key做一个映射到旧节点下标的key -> index表,然后用新vnode的key去找出在旧节点中可以复用的位置。
分析过程:
- 第一步
1
2oldS = a, oldE = c
newS = b, newE = a比较结果:
oldS 和 newE相等,需要把节点a移动到newE所对应的位置,也就是末尾,同时oldS++,newE--
- 第二步
1
2oldS = b, oldE = c
newS = b, newE = e比较结果:
oldS 和 newS相等,需要把节点b移动到newS所对应的位置,同时oldS++,newS++
- 第三步
1
2oldS = c, oldE = c
newS = c, newE = e比较结果:
oldS、oldE 和 newS相等,需要把节点c移动到newS所对应的位置,同时oldS++,newS++
- 第四步
oldS > oldE,则oldCh先遍历完成了,而newCh还没遍历完,说明newCh比oldCh多,所以需要将多出来的节点,插入到真实DOM上对应的位置上
updateChildren的核心原理代码
1 | |
参考来源
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!






