虚拟 DOM

虚拟 DOM 是什么

虚拟 dom 就是一个普通的 js 对象。是一个用来描述真实 dom 结构的 js 对象,包含了 tag、props、children 三个属性。

虚拟 DOM 的结构

现在有一段 Html 代码,如下

1
2
3
<div id="app">
<p class="text">Hello world</p>
</div>

将上面的 Html 代码转换为虚拟 DOM 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
tag: 'div',
props: {
id: 'app'
},
chidren: [
{
tag: 'p',
props: {
className: 'text'
},
chidren: [
'hello world!!!'
]
}
]
}

虚拟 DOM 是树状解构,其节点是 vNode,vNode 和浏览器 DOM 中的 Node 一一对应,通过 vNode 的 elm 属性可以访问带对应的 Node。

虚拟 DOM 的作用

1、我们都知道,传统 dom 数据发送变化的时候,我们都需要不断的去操作 dom,才能更新 dom 的数据,虽然后面出现了模板引擎这种东西,可以让我们一次性去更新多个 dom。但模板引擎依旧没有一种可以追踪状态的机制,当引擎内某个数据发生变化时,他依然要操作 dom 去重新渲染整个引擎。

而虚拟 dom 因为是纯粹的 JS 对象,它可以很好的跟踪当前 dom 状态,因为它会根据当前数据生成一个描述当前 dom 结构的虚拟 dom,然后数据发送变化时,又会生成一个新的虚拟 dom,而这两个虚拟 dom 恰恰保存了变化前后的状态。然后通过 diff 算法,计算出两个前后两个虚拟 dom 之间的差异,得出一个更新的最优方法(哪些发生改变,就更新哪些,只对变化的 DOM 进行操作,而不是更新整个视图),这样,就可以很明显的提升渲染效率以及用户体验

2、因为虚拟 dom 是一个普通的 javascript 对象,故他不单单只能允许在浏览器端,渲染出来的虚拟 dom 可同时在 node 环境下或者 weex 的 app 环境下允许。有很好的跨端性

Vue 中的虚拟 DOM

目前虚拟 dom 的类库有多种,常见的有 snabbdomvirtual-dom, vue 以前用的是 virtual-dom,从 2.x 版本后都是使用的 snabbdom。

虚拟 DOM 中几个核心的方法

  • h 函数
  • patch 函数
  • patchVnode 函数
  • patchChildren 函数

h 函数

1
2
3
4
5
6
export default new Vue({
router,
i18n,
store,
render:(h) => h(App),
}).$mount(`#app`)

在主流的虚拟 DOM 库(snabbdom,virtual-dom)中,通常都有一个 h 函数。

也就是在 React 中的 React.createElement,在 Vue 中的 render 方法中的 createElement。

注:vue 在生命周期 created -> beforeMount 之间的时候会将模板变异成 render 函数,其实就是将模板编译成某种格式放在 render 函数内,然后当 render 函数运行的时候,就会生成虚拟 DOM。

在 React 中通过 babel 将 jsx 转换为 h 函数渲染的形式,而 Vue 是使用 vue-loader 将模板转换为 h 函数渲染的形式。也可以通过 babel-plugin-transform-vue-jsx 插件在 vue 中使用 jsx,本质还是转换为 h 函数渲染的形式。

h函数
h 函数

可见 h 函数使用函数重载的方式定义了多个。

函数重载:函数重载就是定义多个重名函数,利用函数的参数个数以及参数类型来区分。当参数个数不同,参数类型不同时,函数内执行的代码也会相应不同。

图中第四种:

  • 第一个参数 sel 表示 dom 选择器。
  • 第二个参数表示 dom 属性,是个对象。
  • 第三个参数表示子节点,子节点也可以是一个子虚拟节点,也可以是文本节点。
1
2
3
4
5
const vdom = h('div', { class: 'vdom'}, [
h('p', { class: 'text'}, ['hello word']),
h('input', { class: 'ipt', value: '今天星期二' })
]) // 模板就是会编译成这种格式
console.log(vdom)

h 函数内最主要得就是执行了 vnode 函数,vnode 函数得主要作用就是将 h 函数传进来得参数转行为了 js 对象(即虚拟 dom)。

node 函数执行了生成 js 对象(虚拟 dom)的代码。

总结

  • 首先,代码初次运行,开始生命周期
  • 当生命周期走到 created 到 beforeMount 之间的时候,会编译 template 模板成 render 函数。
  • 然后当 render 函数运行时,h 函数被调用,而 h 函数内调用了 vnode 函数生成虚拟 dom,并返回生成结果。虚拟 DOM 首次生成。
  • 之后,当数据发生变化时会重新编译生成一个新虚拟 dom。
  • 根据 diff 算法对新旧两个虚拟 dom 进行对比。