跳到主要内容

Vue 的虚拟 DOM

虚拟 DOM (vdom)和 diff

diff 算法是 vdom 中最核心,最关键的部分

  • DOM 操作非常耗费性能
  • 以前使用 jQuery 可以自行手动控制操作 DOM
  • 而 Vue 是数据驱动视图,如何有效控制 DOM 操作?

解决方案 - vdom

  • 有了一定复杂度,想减少计算次数比较难
  • 那么可以把更多计算转移为 JS 计算,因为 JS 执行速度很快
  • vdom - 用 JS 模拟 DOM 结构,计算出最小的变更,操作 DOM

用 JS 模拟 DOM 结构,举个例子有这样一段 html 结构

<div id="div1" class="container">
<p>vdom</p>
<ul style="font-size: 20px">
<li>a</li>
</ul>
</div>

html 可以看成 xml 结构,通过 json 对象来表示,比如

{
tag: 'div',
props: {
className: 'container',
id: 'div1'
},
children: [
{
tag: 'p',
children: 'vdom'
},
{
tag: 'ul',
props: {style: 'font-size: 20px'},
children: [
{
tag: 'li',
children: 'a'
},
//...
]
}
]
}

通过 snabbdom 学习 vdom

  • 简洁强大的 vdom 库,易学易用
  • Vue 参考它实现的 vdom 和 diff
  • https://github.com/snabbdom/snabbdom
  • Vue 3.0 重新了 vdom 的代码,优化了性能,但 vdom 基本理念不变

snabbdom 库演示

<div id="container"></div>
<button id="btn-change">change</button>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-class.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-props.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-style.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-eventlisteners.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/h.js"></script>
<script>
const snabbdom = window.snabbdom
// 定义 patch
const patch = snabbdom.init([
snabbdom_class,
snabbdom_props,
snabbdom_style,
snabbdom_eventlisteners
])
// 定义 h
const h = snabbdom.h
const container = document.getElementById('container')
// 生成 vnode
const vnode = h('ul#list', {}, [
h('li.item', {}, 'Item 1'),
h('li.item', {}, 'Item 2')
])
patch(container, vnode)
document.getElementById('btn-change').addEventListener('click', () => {
// 生成 newVnode
const newVnode = h('ul#list', {}, [
h('li.item', {}, 'Item 1'),
h('li.item', {}, 'Item B'),
h('li.item', {}, 'Item 3')
])
patch(vnode, newVnode)
// vnode = newVnode // patch 之后,应该用新的覆盖现有的 vnode ,否则每次 change 都是新旧对比
})
</script>