Vue diff原理
最后更新于
最后更新于
真实DOM
Virtual DOM(伪代码)
patch函数主要进行了两个分支流程
判断两节点是否是一样的,如果是则执行 patchVnode
不一样则用 Vnode
替换 oldVnode
关键流程
找到对应的真实 dom,称为 el
判断 Vnode
和 oldVnode
是否指向同一个对象,如果是,那么直接 return
如果他们都有文本节点并且不相等,那么将 el
的文本节点设置为 Vnode
的文本节点
如果 oldVnode
有子节点而 Vnode
没有,则删除 el
的子节点
如果 oldVnode
没有子节点而 Vnode
有,则将 Vnode
的子节点真实化之后添加到 el
如果两者都有子节点,则执行 updateChildren
函数比较子节点,这一步很重要
我们将它们取出来并分别用 s 和 e 指针指向它们的头 child
和尾 child
现在分别对 oldS
、oldE
、S
、E
两两做 sameVnode
比较,有四种比较方式,当其中两个能匹配上那么真实 dom
中的相应节点会移到 Vnode 相应的位置,这句话有点绕,打个比方
如果是 oldS
和 E
匹配上了,那么真实 dom
中的第一个节点会移到最后
如果是 oldE
和 S
匹配上了,那么真实 dom
中的最后一个节点会移到最前,匹配上的两个指针向中间移动
如果四种匹配没有一对是成功的,分为两种情况
如果新旧子节点都存在 key
,那么会根据 oldChild
的 key
生成一张 hash
表,用 S
的 key
与 hash
表做匹配,匹配成功就判断 S
和匹配节点是否为 sameNode
,如果是,就在真实 dom
中将成功的节点移到最前面,否则,将 S
生成对应的节点插入到 dom
中对应的 oldS
位置,oldS
和 S
指针向中间移动。
如果没有 key
,则直接将 S
生成新的节点插入真实 DOM
(ps:这下可以解释为什么 v-for
的时候需要设置 key
了,如果没有 key
那么就只会做四种匹配,就算指针中间有可复用的节点都不能被复用了)
再配个图(假设下图中的所有节点都是有 key
的,且 key
为自身的值)
1.第一步
oldS
和 S
匹配,则将 dom
中的 a
节点放到第一个,已经是第一个了就不管了,此时 dom
的位置为:a b d
2. 第二步
oldS
和 E
匹配,就将原本的 b
节点移动到最后,因为 E
是最后一个节点,他们位置要一致,这就是上面说的:当其中两个能匹配上那么真实 dom
中的相应节点会移到 Vnode 相应的位置,此时 dom 的位置为:a d b
3. 第三步
oldE
和 E
匹配,位置不变此时 dom
的位置为:a d b
4. 第四步
遍历结束,说明 oldCh
先遍历完。就将剩余的 vCh
节点根据自己的的 index
插入到真实 dom
中去,此时 dom
位置为:a c d b
这个匹配过程的结束有两个条件:
oldS > oldE
表示 oldCh
先遍历完,那么就将多余的 vCh
根据 index
添加到 dom
中去(如上图)
S > E
表示 vCh
先遍历完,那么就在真实 dom
中将区间为[oldS, oldE]
的多余节点删掉