上篇文章说到了patch中的新增dom过程,这篇会说diif算法。Vue的diff算法是基于snabbdom的,另外网上也有很多分析的文章,我自己是看的掘金上的篇博客。
在这里我主要会从代码实现上来描述。
入口
在patch函数中,如果新旧 vnode 属于sameVnode,那么就会执行patchVnode过程:
1 | function patch(oldVnode, vnode, hydrating, removeOnly) { |
sameVnode的判断逻辑如下:
1 | function sameVnode(a, b) { |
Vue对于sameVnode的判断逻辑和snabbdom不同,这点可以了解下。真正的patch逻辑在patchVnode函数中。
patchVnode
1 | function patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly) { |
isStatic属性为true的条件是当前节点是静态节点,所以这里vnode和oldVnode都是静态节点。
(isTrue(vnode.isCloned) || isTrue(vnode.isOnce)) 我们在生成render函数字符串中,会有_m或_o,他们分别是renderStatic和markOnce方法(src/core/instance/render-static.js中)。我们的patchVnode是在数据变化后调用,render方法是不变的,只不过因为执行render函数时数据变了,所以生成的vnode对象和之前不同。以_m为例,再次执行_m函数,会直接从vm._staticTrees中获取tree,并通过cloneVNode方法克隆一份出来,这种情况下vnode.isCloned值为true。
addVnodes和removeVnodes用于批量新增、删除dom,这里不再展开。
剩下的主要逻辑是在updateChildren中,它是用于比较两个数组。
updateChildren
1 | function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) { |
这就是diff算法的核心了,已经加上了非常详细的注释。大家可以先参考其他的文章了解diff,然后再来这里看代码的实现会轻松很多。