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 协议 ,转载请注明出处!