Virtual DOM中负责将新旧DOM树中的节点进行对比并找出发生变更的节点这一工作是由diff来进行的,diff是Virtual DOM较为核心的部分,要对比两棵层级复杂的DOM树,diff的时间复杂度直接影响了新旧节点替换的性能。Vue实践的diff算法仅仅对同级的节点进行比较,因此时间复杂度为O(n)。如下图,diff算法仅对颜色相同的方框中的节点进行对比。
Diff算法首先对比新旧节点,这一对比仅在同层节点间进行。由于对比过程源码比较复杂且篇幅巨大,有兴趣的同学可以去github上看看,这里仅对算法进行解析,因此就不搬运源码了。
需要说明一下,在接下来的对比算法中,相同节点的概念,指的是两个节点的key、tag等在第一次渲染时打上的各种标识唯一DOM界的的标记、属性均一致,而不是包含它所带的值,样式颜色等。Patch操作说的是将两个节点进行对比将发生变化的一些属性更新,如果两个节点均包含子节点,那么对他们的子节点同样进行diff对比。在找出同层的两组新旧节点后,分别为他们打上开始和结束的标志,在对比过程中,开始和结束的标志不断向中间靠拢,直到新节点队列或旧节点队列中有一个的开始标志到结束标志之后,那么对比就完成了,整个对比过程如下图:
算法首先将四个被打了标记的节点做如下六种情况对比:
一、NewStart和OldStart
如果是同一节点那么直接将这两个节点进行patch操作,NewStart和OldStart标志后移到下一个节点
二、NewStart和OldEnd
如果是同一节点,将OldEnd节点移到OldStart前,标志前移一个节点,NewStart后移一个节点
三、NewEnd和OldStart
如果是同一节点,将OldStart节点移到OldEnd后,OldStart后移一个节点,NewEnd前移一个节点
四、NewEnd和OldEnd
如果是同一节点那么直接将这两个节点进行patch操作
五、与NewStart相同的节点在旧节点队列中
如果以上情况皆不满足,那么就在旧的节点队列中进行一次遍历对比,找出与NewStart相同的节点,后将该节点前移到OldStart前
六、旧节点队列中无与NewStart相同的节点
如果在旧的节点队列中找不到与NewStart相同的节点,那么就直接在OldStart前直接插入NewStart节点。
按照上面的规则一直对比,直到NewStart到NewEnd相遇或OldStart到OldEnd相遇,此时如果新节点队列中仍有没匹配到的节点,那么就将它们插入旧的节点队列中去,如果旧的节点队列中仍有未匹配到的节点,那么就删掉他们。
在完成对比后,将新的DOM树也就完成了一次更新。
Vue的diff算法主要实现思路大致如此,如果要完整的了解他还是需要阅读大量的源码的,有进阶想法的同学可以去看看。
VUE框架相对学习成本低,易上手且高效灵活在企业项目开发中比较受欢迎。当前,普元的DevOps、微服务、容器云等产品前端技术均使用了VUE框架。学习其底层运行原理还是有助于提升开发人员的编码技能的。
推荐阅读
DevOps之动态表单——优雅地把工作量甩给后端
那些Vue开发遇到的坑---响应式系统
DevOps平台之看板设计