持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第23天,点击查看活动详情
众所周知,操作一次dom需要重绘整个视图,需要经历创建 DOM 树 —> 创建 Style Rules -> 构建 Render 树 —> 布局 Layout -—> 绘制 Painting这一过程,是相当耗费性能。而Vue2 内部使用的虚拟DOM就是改造的 Snabbdom,以此来作为一种提升性能的解决方案。
虚拟dom基础
因此Vue.js将DOM抽象成一个以js对象为节点的虚拟Dom,简单而言就是由普通的JS对象来描述DOM对象。
那么使用虚拟dom的好处和作用是什么呢?
1.实际上通过虚拟dom改变真正的dom并不比直接操作dom效率更高。而是页面的更新可以先全部反映在这个 JS对象 上,通过对比新旧vnode,找到真正需要更新的地方,再去进行真实的dom操作,相比整个替换算有dom,从而提升性能。
2.虚拟DOM是对真实DOM的一层抽象,不依赖某个平台,它可以是浏览器平台,也可以是weex,甚至是node平台也可以对这样一棵抽象DOM树进行创建删除修改等操作,这也为前后端同构提供了可能。
由以上介绍可知,虚拟DOM就是通过JS来生成一个AST节点树,接下来可以看一下一个虚拟dom是什么样子的?
真实的模板:
<div class="test">
<span class="name">hello,vivi</span>
</div>
用来描述上述真实dom的虚拟dom
{
tag: 'div'
data: {
class: 'test'
},
children: [
{
tag: 'span',
data: {
class: 'name'
}
text: 'hello,vivi'
}
]
}
源码分析
VNode基类
/src/core/vdom/vnode.ts
VNode的属性虽然很多,但是我们主要了解 tag ,data, 属性, children 属性, text 属性,elm 属性,key 属性即可。
创建一个空的VNode
/src/core/vdom/vnode.ts
创建一个文本节点
创建一个组件节点
/src/core/vdom/create-component.ts
克隆一个VNode节点
/src/core/vdom/vnode.ts
创建一个虚拟节点
createElement()方法主要是用来创建一个虚拟节点。
通过源码可以看到当data上已经绑定__ob__的时候,代表该对象已经被响应式处理过了,所以创建一个空节点。
当tag不存在的时候同样创建一个空节点。
当tag不是一个String类型的时候代表tag是一个组件的构造类,则直接用new VNode创建。
当tag是String类型的时候,如果是保留标签,则用new VNode创建一个VNode实例,如果在vm的option的components找得到该tag,代表这是一个组件,否则统一用new VNode创建。
创建 VNode 过程
首先初始化vue,即new Vue()过程,调用this._init(options)方法-->在该初始化方法中通过$mount 实例方法去挂载 dom 的-->在$mount 方法会去调用 mountComponent 方法,在这个方法中先实例化一个渲染Watcher,在它的回调函数中会调用 updateComponent 方法,在此方法中调用 vm._render 方法先生成虚拟 Node,最终调用 vm._update 更新 DOM。
src/core/instance/lifecycle.js
src/core/instance/render.js
可以看到,vue利用 _createElement 方法创建 VNode。可以看回上文的_createElement方法的详解。