第5章 虚拟DOM简介
如图是DOM的整体运行流程,向将vnode与oldVnode做对比,然后再更新视图。
可以看出,虚拟DOM在vue.js中主要做了两件事:
- 提供与真实DOM 节点所对应的虚拟节点vnode。
- 将虚拟节点vnode和旧虚拟节点oldVnode进行对比,然后更新视图。
虚拟DOM是将状态映射成视图的众多解决方案的一种,它的运作原理是使用状态生成虚拟节点,然后使用虚拟节点渲染视图。
之所以需要先使用状态生成虚拟节点,是因为如果直接用状态生成真是DOM,会有一定程度的性能浪费。而先创建虚拟节点再渲染视图,就可以将虚拟节点缓存,然后使用新创建的虚拟节点和上一次渲染时缓存的虚拟节点进行对比,然后根据对比结果只更新需要更新的真实DOM节点,从而避免不必要的DOM操作,节省一定的性能开销。
由于Vue.js的变化侦测粒度更细,所以当状态发生变化时,Vue.js知道的信息更多,一定程度上可以知道哪些位置使用了状态,因此,Vue.js可以通过细粒度的绑定来更新视图,Vue.js1.0就是这样实现的。
但是这样做有一定的代价。因为粒度太细,就会有很多watcher同时观察某些状态,会有一些内存开销以及一些依赖追踪的开销,所以Vue.js采取了一个中等粒度的解决方案,状态针刺不再细化到某个具体节点。而是某个组件,组件内部通过虚拟DOM来渲染视图,这可以大大缩减依赖数量和watcher数量。
Vue.js通过模板来描述状态与视图之间的映射关系,所以它会先将模板编译成渲染函数,然后执行渲染函数生成虚拟节点,最后使用虚拟节点更新视图。
因此,虚拟DOM在Vue.js中所做的事是提供虚拟节点vnode和对新旧两个vnode进行对比,并根据此对比结果进行DOM操作更新视图。
第6章 VNode
目录: 6.1 什么是VNode
6.2 VNode的作用
6.3 VNode的类型
6.4 总结
6.1 什么是VNode
在Vue.js中存在一个VNode类,使用它可以实例化不同类型的vnode实例,而不同类型的vnode实例各自表示不同类型的DOM元素。
例:DOM元素有元素节点。文本节点和注释节点等,vnode实例也会对应着有元素节点、文本节点和注释节点等。
简单的说,vnode可以理解成节点描述对象,它描述了应该怎样去创建真实的DOM节点。
如图,展示了使用vnode创建真实DOM并渲染到视图的过程。
从上图可以看出vnode和视图是一一对应的,我们可以把vnode理解成JavaScript对象版本的DOM元素。
从上图得知,渲染视图的过程:1、创建vnode ===》2、使用vnode去生成真实的DOM元素 ===》3、插入到页面渲染视图
6.2 VNode的作用
作用:对vnode进行缓存,并将上一次缓存的vnode和当前新创建的vnode进行对比,只更新变化的节点
Vue.js目前对状态的侦测策略采用了中等粒度。当状态发生变化时,只通知到组件级别,然后组件内使用虚拟DOM来渲染视图。
如上图,通知了第二个组件,只要组件使用的众多状态中有一个发生了变化,那么整个组件的所有节点就要重新渲染。很明显会造成很大的性能浪费。因此,对vnode进行缓存,并将上一次缓存的vnode和当前新创建的vnode进行对比,只更新发生变化的节点就变得尤为重要。这就是vnode最重要的一个作用。
6.3 VNode的类型
vnode的类型:
- 注释节点
- 文本节点
- 元素节点
- 组件节点
- 函数式组件
- 克隆节点
6.4总结
VNodes是一个类,可以生成不同类型的vnode实例,而不同类型的vnode表示不同类型的真实DOM元素。
由于Vue.js对组件采用了虚拟DOM来更新视图,当属性发生变化时,整个组件都要进行重新渲染的操作,但组件内并不是所有DOM节点都需要更新,所以vnode缓存并将当前新生成的vnode和上一次缓存的oldVnode进行对比,只对需要更新的部分进行DOM操作,可以提升很多性能。
vnode有很多类型,它们本质上都是从VNode类型实例化出的对象,其唯一区别只是属性不同。
第7章 patch
虚拟DOM最核心的部分是patch,它可以将vnode渲染成真实的DOM。
patch也可以叫作patching算法,通过它渲染真实DOM时,并不是暴力覆盖原来DOM,而是对比新旧两个vnode之间有哪些不同,然后根据对比结果找出需要更新的节点进行更新。
为什么这么做?因为DOM操作的执行速度远不如JavaScript的运算速度快。
因此把大量的DOM操作搬到JavaScript中,使用patching算法来计算出真正需要更新的节点,最大限度地减少DOM操作,从而显著提升性能。这本质上其实是使用JavaScript的运算成本来替换DOM操作的执行成本,而JavaScript的运算速度要比DOM快很多,这样做很划算,所以才有虚拟DOM。
7.1 patch介绍
通过patch对比两个vnode之间的差异,达到修改DOM(也可以理解为渲染视图)的目的。
patch不是暴力替换节点,而是在现有DOM上进行修改来达到渲染时图的目的。对现有DOM进行修改需要做三件事:
- 创建新增的节点;
- 删除已经废弃的节点
- 修改需要更新的节点;
patch的过程其实就是创建节点、删除节点和修改节点的过程。
7.1.1新增节点
- 在什么情况下新增节点?
- oldVnode不存在而vnode存在时,就需要使用vnode身材高恒真实的DOM元素并将其插入到视图当中去。
- 通常发生在首页渲染中,因为首次渲染时,DOM中不存在任何节点,所以oldVnode是不存在的。
- 当vnode和oldVnode完全不是同一个节点时,需要使用vnode生成真实的DOM元素并将其插入到视图当中。
- 此时使用vnode创建一个新DOM节点,用它去替换oldVnode所对应的真实DOM节点
- 为何讨论在什么情况下新增节点?
- 本质上是为了使用JavaScript的计算成本来换取DOM的操作成本。如果一个节点已经存在于DOM中,那就不需要重新创建一个同样的节点去替换已经存在的节点。
- 事实上,只有那些因为状态的改变而新增的节点在DOM中并不存在时,我们才需要创建一个节点并插入到DOM中。
7.1.2 删除节点
- 以vnode为标准,只在oldVnode中存在,在vnode中不存在的节点都属于被废弃的节点,而被废弃的节点需要从DOM中删除。
- 如图7.3所示,替换的过程是将新创建的DOM节点插入到旧节点的旁边,然后再将旧节点删除,从而完成替换过程。
7.1.3更新节点
新旧两个节点是同一个节点时,需要对这两个节点进行比较细致的对比,然后对oldVnode在视图中所对应的真实节点进行更新。
详细对比过程在7.4节中。
7.1.4 小结
整个patch的过程