虚拟DOM简介
Posted vue和react的渐入佳境
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了虚拟DOM简介相关的知识,希望对你有一定的参考价值。
1什么事虚拟DOM
虚拟DOM是随着时代发展而诞生的产物。
现在,我们使用的三大主流框架vue.js,Angular和React都是声明式操作DOM。我们通过描述状态和DOM之间的映射关系是怎样的,就可以将状态渲染成视图。关于状态到视图的转换过程,框架会帮我们做,不需要我们自己手动去操作DOM。
状态可以使javascript中的任意类型。Object,Array,String,Number,Boolean等都可以作为状态,这些状态可能最终会以段落,表单,链接或按钮等元素呈现在用户界面上,具体地说是呈现在页面上。
本质上,我们将状态作为输入,并生成DOM输出到页面上显示出来,这个过程叫做渲染。
然而通常程序在运行时,状态会不断发生变化(引起状态变化的原因有很多,有可能是用户点击了某个按钮,也可能是某个ajax请求,这些行为都是异步发生的。理论上,所有异步行为都有可能引起状态变化)。每当状态发生变化时,都需要重新渲染。如何确定状态中发生了什么变化以及需要在哪里更新DOM?
在这种情况下,最简单粗暴的解决方式是,既不需要关心状态发生了什么变化,也不需要关心在哪里更新DOM,我们只需要把所有DOM全删了,然后使用状态重新生成一份DOM,并将其输出到页面上显示出来就好了。
但是访问DOM是非常昂贵的。按照上面说的方式做,会造成相当多的性能浪费。状态变化通常只有有限几个节点需要重新渲染,所以我们不仅需要找出哪里需要更新,还需要尽可能少地访问DOM。
当某个状态发生变化时,只更新与这个状态相关联的DOM节点。
这个问题有很多种解决方案。目前,各大主流框架都有自己的一套解决方案,在Angular中就是脏检查的流程,React中使用虚拟DOM,Vue.js1.0通过细粒度的绑定。因此,虚拟DOM本质上只是众多解决方案中的一种,可以用但并不一定必须用。
虚拟DOM的解决方式是通过状态生成一个虚拟节点树,然后使用虚拟节点树进行渲染。在渲染之前,会使用新生成的虚拟节点树和上一次生成的虚拟节点树进行对比,只渲染不同的部分。
虚拟节点树其实是由组件建立起来的整个虚拟节点树。
2为什么要引入虚拟DOM
事实上,Angular和React的变化侦测有一个共同点,那就是他们都不知道那些状态(state)变了。因此,就需要进行比较暴力的对比,React是通过虚拟DOM的对比,Angular是使用脏检查的流程。
Vue.js的变化侦测和他们都不一样,它在一定程度上知道具体的那些状态发生了变化,这样就可以通过更细粒度的绑定来更新视图。也就是说,在Vue.js中,当状态发生变化时,它在一定程度上知道哪些节点使用了状态,从而对这些节点进行更新操作,根本不需要对比。事实上,在Vue.js1.0的时候就是这样实现的。
但是这样做其实也是有一定代价的,因为粒度太细。每一个绑定都会有一个对应的watcher来观察状态的变化,这样就会有一些内存开销以及一些依赖追踪的开销。当状态被越多的节点使用时,开销就越大。对于一个大型项目来说,这个开销是非常大的。
因此,Vue.js2.0开始选择了一个中等粒度的解决方案,那就是引入了虚拟DOM。组件级别是一个watcher实例,就是说即便一个组件内有10个节点使用了某个状态,但其实也只有一个watcher在观察这个状态的变化。所以当这个状态发生变化时,只能通知到组件,然后组件内部通过虚拟DOM去进行对比和渲染。这是一个比较折中的方案。
Vue.js之所以能随意调整绑定的粒度,本质上还要归功于变化侦测。关于Vue.js的变化侦测原理,之前文章已有讲解。
3Vue.js中的虚拟DOM
在vue.js中,我们使用模板来描述状态与DOM之间的映射关系。vue.js通过编译将模板转换成渲染函数(render),执行渲染函数就可以得到一个虚拟节点树,使用这个虚拟节点树就可以渲染页面。
虚拟DOM的终极目标是将虚拟节点(vnode)渲染到视图上。但是如果直接使用虚拟节点覆盖旧节点的话,会有很多不必要的DOM操作。
例如,一个ul标签下有很多li标签,其中只有一个li有变化,这种情况下如果使用新的ul去替换旧的ul,其实除了那些发生了变化的li节点之外,其他节点都不需要重新渲染。
由于DOM操作比较慢,所以这些DOM操作在性能上会有一定的浪费,减免这些不必要的DOM操作,会提升很大一部分性能。
为了避免不必要DOM操作,虚拟DOM在虚拟节点映射到视图的过程中,将虚拟节点与上一次渲染视图所使用的旧虚拟节点(oldVnode)作对比,找出真正需要更新的节点来进行DOM操作,从而避免操作其他无任何改动的DOM。
虚拟DOM的整体运行流程,先将vnode与oldVnode作对比,然后再更新视图。
可以看出,虚拟DOM在Vue.js中所做的事情其实并没有想象中的那么复杂,它主要做了两件事。
1提供与真实DOM在Vue.js节点所对应的虚拟节点vnode。
2将虚拟节点vnode和旧虚拟节点oldVnode进行对比,然后更新视图。
vnode是JavaScript中一个很普通的对象,这个对象的属性上保存了生成DOM节点所需要的一些数据,我们再往后的文章将会介绍。
对两个虚拟节点进行对比是虚拟DOM中最核心的算法(即patch),它可以判断出哪些节点发生了变化,从而只对发生了变化的节点进行更新操作。关于patch,我们也会在后面的文章中慢慢介绍。
以上是关于虚拟DOM简介的主要内容,如果未能解决你的问题,请参考以下文章