当数据发生变化时,vue是怎么运作的?
vue会根据真实DOM生成virtual dom,当虚拟DOM节点数据变化时会生成一个新的vnode,然后vnode和oldVnode作对比,如果有不一样的地方就直接修改在真实的dom上,然后将oldVnode更新为vnode,diff的过程就是调用patch(打补丁)函数,比较新旧节点,一边比较一边给真实的DOM打补丁
虚拟DOM和真实DOM都是什么样的?
<div>
<span>我是真实DOM<</span>
</div>
let vnode = {
tag: 'div',
children: [
{tag: 'span', text: '我是虚拟DOM'}
]
}
vue的diff只会比较新旧节点,并且只会在同级比较,不会进行跨层级比较
diff过程
在学习diff的过程前,我们需要知道当数据发生变化,会触发obderve中的set方法,set方法会调用Dep.notify通知所有订阅者watcher,订阅者就会调用patch给真实的DOM打补丁(对Vue的observe、watch、compile概念不清楚的可以去看我的vue双向绑定原理文章)
在patchVnode方法中还调用了一个方法updateChildren
-
这个方法将vnode的子节点vCh和oldVnode的子节点oldCh作对比进行更新,简单来说就是,将旧节点集合以新节点集合为标准调整 -
oldCh和vCh各有两个头尾的变量StartIdx和EndIdx,它们的2个变量相互比较,一共有4种比较方式。如果4种比较都没匹配,如果设置了key,就会用key进行比较,在比较的过程中,变量会往中间靠,一旦StartIdx>EndIdx表明oldCh和vCh至少有一个已经遍历完了,就会结束比较
大家来跟我看图就明白了 oldVnode是更新前的节点,vnode是更新后的节点,那么我们来看看,旧节点集合是怎么根据新节点集合来进行调整的
第一步:
现在分别对oldS、oldE、S、E两两做sameVnode比较,有四种比较方式,当其中两个能匹配上那么真实dom中的相应节点会移到Vnode相应的位置
- 如果是oldS和E匹配上了,那么真实dom中的第一个节点会移到最后
- 如果是oldE和S匹配上了,那么真实dom中的最后一个节点会移到最前,匹配上的两个指针向中间移动
- 如果四种匹配没有一对是成功的,那么遍历oldChild,S挨个和他们匹配,匹配成功就在真实dom中- 将成功的节点移到最前面,如果依旧没有成功的,那么将S对应的节点插入到dom中对应的oldS位置,oldS和S指针向中间移动。
第二步: a = oldS, c = oldE a = s, b = e oldS和s匹配上了,但是都在第一个,所以不用做任何操作 此时DOM为a b d ,最后oldS指针和s指针向内部移动oldS指向旧集合的b,s指向新集合的c
第三步: b = oldS, d = oldE c = s, b = e 发现oldS指针和e指针指向的内容匹配,所以将旧集合中的b元素移动到最后 (当其中两个能匹配上那么真实dom中的相应节点会移到Vnode相应的位置) 此时DOM为a d b ,最后oldS指针和e指针向内部移动oldS指向旧集合的d,e指向新集合的d
第四步: d = oldE, d = oldS c = s, d = e 发现oldE和d是匹配的,位置不变 然后oldS++ ,oldE-- oldS > oldE,遍历结束,说明oldCh先遍历完,将剩余的节点按照自己的index插入到真是dom中,此时DOM为a d d b
|