出自深入浅出Vue.js
在Vue.js中,我们使用模板来描述状态与DOM之间的映射关系,Vue.js通过编译模板转换成渲染函数(render),执行渲染函数就可以得到一个虚拟节点树,使用这个虚拟节点树就可以渲染页面。
模板---------------->渲染函数------------------>vnode-------------------->视图
虚拟DOM的最终目标是将虚拟节点(vnode)渲染到视图上。但是如果直接使用虚拟节点覆盖旧节点的话,会有很多不必要的DOM操作。
例如,一个ul标签下有很多li标签,其中只有一个li有变化,这种情况下如果使用新的ul去替换旧的ul,其实除了那个发生变化的li节点之外,其他节点都不需要重新渲染。
由于DOM操作太慢,所以这些DOM操作性能上会有一定的浪费,避免这些不必要的DOM操作会提升很大一部分性能。
为了避免不必要的DOM操作,虚拟DOM在虚拟节点映射到视图的过程中,将虚拟节点与上一次渲染视图所使用的旧虚拟节点做对比,找出真正需要更新的节点来进行DOM操作,从而避免操作其他
无任何改动的DOM。
1.提供与真实DOM节点所对应的虚拟节点vnode。
2.将虚拟节点vnode和旧虚拟节点oldVnode进行对比,然后更新视图。
vnode是Javascrpit中一个很普通的对象,这个对象的属性上保存了生成DOM节点所需要的一些数据 对两个虚拟节点进行对比是虚拟DOM中最核心的算法(即patch),它可以判断出那些节点发生了变化,从而对发生变化的节点进行更新操作。
总结
虚拟DOM是将状态映射成视图的众多解决方案中的一种,它的运作原理是使用状态生成虚拟节点,然后使用虚拟节点渲染成视图。
之所以需要先使用状态生成虚拟节点,是因为如果直接用状态生成真实DOM,会有一定程度的性能浪费,而先创造虚拟节点早渲染视图,就可以将虚拟节点缓存,然后使用新创建的虚拟节点和上一次渲染时缓存的虚拟节点进行对比,然后根据对比结果只更新需要更新的真实DOM节点,从而避免不必要的DOM操作,节省一定的性能开销。
由于Vue.js的变化侦测粒度更细,所以当状态发生变化时,Vue.js知道的信息更多,一定程度上可以知道那些位置使用了状态。因此,Vue.js通过细粒度的绑定来更新视图,Vue.js1.0就是这样实现的。
但是这样做也有一定的代价。因为粒度太细,就会有很多watcher同时观察某些状态,会有一些内存开销以及一些依赖的开销,所以Vue.js2.0采取了一个中等粒度的解决方案,状态侦测不再细化到某个具体节点,而是某个组件,组件内部通过虚拟DOM来渲染视图,这可以大大缩减依赖数量和watcher数量。
Vue.js中通过模板来描述状态与视图之间的映射关系,所以它会先将模板编译成渲染函数,然后执行渲染函数生成虚拟节点,最后使用虚拟节点更新视图。
因此,虚拟DOM在Vue.js中所做的事是提供虚拟节点vnode和对新旧两个vnode进行对比,并且根据比对结果进行DOM来更新视图。
什么VNode
在vue.js中存在一个vnode类,使用它可以实例化不同类型的vnode,而不同类型的vnode实例各自表示不同类型的DOM元素
例如,DOM元素有元素节点,文本节点和注释节点等,vnode实例也会对应着有元素节点,文本节点,注释节点等
简单地说,vnode可以理解成节点描述对象,它描述了应该怎样去创建真实的DOM节点。
VNode的作用
由于每次渲染视图时都是先创建vnode,然后使用它创建真实DOM插入到页面中,所以可以将上一次渲染视图时创建的vnode缓存起来,之后每当需要重新渲染视图时,将新创建的vnode和上一次缓存的vnode进行对比,查看他们之间有那些不一样的地方,找出这些不一样的地方基于此去修改真实的DOM
Vue.js目前对状态的侦测策略采用了中等粒度,当状态发生变化时,只通知到组件级别,然后组件内使用虚拟DOM来渲染视图
也就是说,只要组件使用的众多状态有一个发生了变化,那么整个组件就要重新渲染。
如果组件只有一个节点发生了变化,那么重新渲染整个组件的所有节点,很明显会造成性能的很大浪费, 因此对vnode进行缓存,并将上一次缓存的vnode和当前新创建的vnode进行对比,只更新发生变化的节点变得尤为重要,这也是vnode最重要的一个作用
VNode的类型
vnode有很多不同的类型,接下来我们介绍不同类型之间有什么区别
注释节点
文本节点
元素节点
组件节点
函数式组件
克隆节点
总结
Vnode是一个类,可以生成不同类型的vnode实例,而不同类型的vnode表示不同类型的真实DOM元素
由于Vue.js对组件采用了虚拟DOM更新视图,当属性发生变化时,整个组件都要进行重新渲染组件的操作,但组件内不是所有DOM节点都需要更新 ,所以将vnode缓存兵器将新生成的vnode和上一次缓存的oldvnode进行对比,只对需要更新的部分进行DOM操作可以提升很多性能
vnode有多种类型,它们本质上都是从哪个VNode类实例化出来的对象,其唯一区别是属性不同。