虚拟DOM的本质
什么是DOM
- DOM:简单来讲,就是一种数据格式。好比JSON。
- 正常开发网页,需要使用HTML,CSS,JavaScript。相信大家都知道这三种语言的作用
- 关键就是HTML,对于HTML文件,浏览器收到后,会有浏览器内部的渲染引擎去解析,转换
- 比如Chrom的渲染引擎Blink,他会解析HTML中的标签,文本各个节点,将整个HTML文件转换为一个DOM节点树
- 它既不是HTML语法,也不是JavaScript语法,它是一种用于表述HTML文件的数据模型,如下所示
// 这就是HTML转换后的DOM
Document
├── html
│ ├── head
│ │ ├── title
│ │ │ └── "My Webpage"
│ │ └── style
│ │ └── "body { background-color: #f0f0f0; } h1 { color: blue; }"
│ └── body
│ ├── h1
│ │ └── "Welcome to my webpage!"
│ ├── p
│ │ └── "This is a paragraph of text."
│ └── ul
│ ├── li
│ │ └── "Item 1"
- DOM的作用:单纯的HTML文件,不支持Javascript控制,但是转换为DOM这种数据格式,既可以被渲染器解析渲染,又可以被Javascript引擎解析控制。
什么是虚拟DOM
-
虚拟DOM:简单来讲,也是一种数据格式。它采用是JavaScript语言中的对象来表示
- 正常情况,我们JavaScript控制页面渲染,是通过浏览器提供的接口去操作DOM
- 关键就是DOM,对于DOM,我们通过浏览器提供的接口如,getElementById就可以获取到
- 获取到DOM节点后,我们可以使用JavaScript语言去操作。把它当成一个普通的Javascript对象就行
- 而虚拟DOM,是真正的JavaScript对象。
-
虚拟DOM的作用:操作真实DOM,需要通过浏览器提供的接口去获取DOM,虚拟DOM,是由Javascript对象模拟的真实DOM。
- 在早期,前端页面很少有逻辑,基本是静态页面,那就不需要JavaScript获取控制页面的渲染
- 而随着Ajax的流行,许多逻辑被放到了前端页面,为了控制页面的动态渲染,就需要大量的操作浏览器提供的DOM接口。然而JavaScript执行逻辑是很快的,但是渲染器重新渲染则非常耗时。
- 稍微提一下,之所以说重新渲染非常耗时,是相对于js执行代码的速度来说的,每次重新渲染某个节点,除了节点本身外,其周围的节点也大概率会受影响。因此要避免重新渲染,或尽量减少对其他节点的影响
- 而使用虚拟DOM,我们就可以使用各种算法先分析这个虚拟DOM,将其需要重新渲染的部分尽量减少。分析完成后,再去操作真实的DOM,这样就能优化页面的渲染效率。
- 那虚拟DOM到底长什么样呢,其实就是一个普通对象,如下所示
{
tag: 'div',
children: [
{
tag: 'h1',
children: [
{
text: '{{ title }}'
}
]
},
{
tag: 'p',
children: [
{
text: '{{ content }}'
}
]
}
]
}
什么是VNode
- 虚拟DOM是我们通过普通对象来模拟真实DOM的一种数据结构
- 观察上述真实DOM的结构可以发现,DOM树是由不同的节点组成的。
- VNode是描述DOM节点的JavaScript描述对象,与VDOM一样,只是描述的对象不同
- VNode常见的类型与DOM节点基本对应,比如注释节点,文本节点,元素节点。
- VNode是一个对象,不同类型的VNode只是对象的某些属性值不同,因此将VNode抽象成类。
- 不同类型的VNode可以去继承通用的属性,或重写某些属性。VNode类的代码如下
export default class VNode {
constructor (tag, data, children, text, elm, context, componentOptions, asyncFactory) {
this.tag = tag
this.data = data
this.children = children
this.text = text
this.elm = elm
this.ns = undefined
this.context = context
this.functionalContext = undefined
this.functionalOptions = undefined
this.functionalScopeId = undefined
this.key = data && data.key
this.componentOptions = componentOptions
this.componentInstance = undefined
this.parent = undefined
this.raw = false
this.isStatic = false
this.isRootInsert = true
this.isComment = false
this.isCloned = false
this.isOnce = false
this.asyncFactory = asyncFactory
this.asyncMeta = undefined
this.isAsyncPlaceholder = false
}
get child () {
return this.componentInstance
}
}
什么是Patch
- patch是通过vnode和vdom来分析需要重新渲染的部分,然后去通过DOMAPI操作真实的DOM。
- 一般是采用vnode的方式来更新渲染,如果采用vdom,也就是更新整个页面,但其实我们可能只修改了某些节点,此时修改vnode是最小的成本。
- 为了找出新旧vnode的不同,采用了diff算法来比较两个对象的属性。具体可看后续文章分析
总结
- VNode是一个用于生成不同类型的vnode实例的类。
- 不同vnode实例用来描述不同的真实DOM节点,他们之间的区别只是属性不同
- Vue.js更新视图是通过将组件中的template解析为一个个的vnode实例,比较数据更新前后vnode的变化。将需要更新的部分,以最小开销渲染到页面