一、什么是虚拟DOM
web早期,不需要频繁的操作DOM,使用Jquery来开发就可以满足需求。随着时代的发展,DOM操作越来越频繁,如果用原来的办法的话程序中的状态很难管理,这就是命令式操作DOM的问题,而Vue是声明式操作DOM,我们只需要描述状态和DOM之间的关系就可以。
访问DOM是非常昂贵的,如果直接不关心状态变化,直接把DOM全删了,然后再重新生成一份DOM是非常消耗性能的,为了解决这个问题,各大框架都有自己的一套方案:Angular是脏检查,React是虚拟DOM,vue1是通过细粒度的绑定,vue2就采用了中等粒度的办法,引入虚拟DOM。
虚拟DOM其实就是通过状态生成一个虚拟节点数,然后使用虚拟节点树进行渲染,渲染之前,新生成的虚拟节点数会和上一次的进行对比,只渲染不同的部分
二、为什么要引入虚拟DOM
Angular和React是不知道哪些状态变了的,所有状态改变后就需要通过暴力对比,而vue不一样,状态变化后就知道哪个节点使用了这个状态,不需要对比,vue1就是这样实现的。
但vue1的粒度太细致,项目越大,依赖追踪的开销就越大,所有vue2使用了中等粒度的方案,引入了虚拟DOM,状态变化只通知到组件,组件内部再进行虚拟DOM渲染。
虚拟DOM在vue中所做的事就说提供vnode和对新旧两个vnode进行对比,并将对比结果进行DOM操作来更新视图。
三、什么是Vnode
vue中存在一个VNode类,用来实例化不同类型的vode实例(就说js中的一个对象),它描述了怎么去创建真实的DOM。
vode的类型包括:
(1)注释节点
(2)文本节点
(3)元素节点
(4)组件节点
(5)函数式节点
(6)克隆节点
四、patch
虚拟DOM的最核心部分就是patch,它将vnode渲染成真实的DOM,patch的过程其实就是创建节点、删除节点、修改节点的过程,当oldVnode和vnode不一样时,以vnode的标准来渲染视图。
1.新增节点:
(1)发生在首次渲染中
(2)当vnode和oldVnode完全不是同一个节点的时候,vnode就是全新节点,oldVnode就是废弃节点。使用createElement和appendChild
2.删除节点:当vnode中不存在的节点都是废弃节点,要删除。使用removeNode
3.更新节点:两个节点是同一个节点,要对两个节点进行更细致的对比,然后对oldVnode在视图中对应的视图进行更新。更细致的对比后面单独说,很重要
更新节点详情:
(1)先判断新旧两个节点是否是静态节点,是则跳过更新操作
(2)如果节点有文本属性,则看文本是否相同,不同直接setTextContent方法更新文本
(3)如果是元素节点的话,看是否有children属性(也就是子节点)
如果没有子节点就是空节点,则oldVnode对应视图中有什么删除什么
如果有子节点,就要对新旧两个虚拟节点的子节点进行一个详细对比并且更新(体现在updateChildren函数内):
updateChildren是深度优先,同层对比,其优化策略为:
有四种快捷查找方式:
(1)新前和旧前
(2)新后和旧后
(3)新后和旧前
(4)新前和旧后
新前:是newChildren中所有未处理的第一个节点 旧前:是oldChildren中所有未处理的第一个节点 新后:是newChildren中未处理的最后一个节点 旧后:是oldChildren中未处理的最后一个节点
这样可以很大程度的避免循环oldChildren来查找节点
优化后就是从两边向中间循环:利用oldStartIdx,oldEndIdx,newStartIdx,newEndIdx四个变量实现,每处理一个节点,就将下标向指定方向移动一个位置