虚拟DOM(Virtual DOM)简称 vdom,是实现 Vue 的重要基石。
众所周知,DOM 操作非常耗时,相对来说, JS 的操作是很快的。在 Vue 等框架之前,我们一般是使用 jQuery,jQuery 可以自行控制 DOM 操作的时机,手动调整。而 Vue 是数据驱动视图,一般都是操作数据,而不会直接操作 DOM,此时我们应该如何有效操作 DOM 呢?
解决方案:vdom。vdom 最初是由 React 提出的,后来得到了大力地普及。由于现在的页面越来越复杂,想减少页面的计算次数比较困难,因此把更多的计算转移为 JS 计算,因为现在 JS 的执行速度非常快。vdom 就是使用 JS 模拟 DOM 结构,计算出最小的变更,再操作 DOM。
使用 JS 模拟 DOM 结构,示例如下:
1 2 3 4 5 6 7
| <div id="div1" class="container"> <p>hello</p> <ul style="font-size: 20px"> <li>a</li> </ul> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| { tag: 'div', props: { id: 'div1', className: 'container', }, children: [ { tag: 'p', children: 'hello' }, { tag: 'ul', props: { style: 'font-size: 20px' }, children: [ { tag: 'li', children: 'a' } ] } ] }
|
(以上的写法并不是唯一的写法,不同的框架写法可能不一样,但是都大同小异,都是主要包括标签名、属性、子节点。)
使用 JS 模拟了 DOM 结构之后,应该如何去使用呢?接下来我们通过 snabbdom 学习 vdom。snabbdom 是一个简洁强大的 vdom 库,Vue 就是参考它实现的 vdom 和 diff。
snabbdom 官网:https://github.com/snabbdom/snabbdom
(注意:Vue3.0 中重写了 vdom 的代码,优化了性能,可能和现在 snabbdom 的实现方式有所不同,但是 vdom 的基本理念不变。)
snabbdom 的使用重点是:① h函数(根据传递进来的参数,返回一个 vnode 对象);② vnode 对象(用来表示相应的 dom 结构);③ patch 函数(找到最小更新范围并更新 DOM 节点)。
看一个使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <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
const patch = snabbdom.init([ snabbdom_class, snabbdom_props, snabbdom_style, snabbdom_eventlisteners ])
const h = snabbdom.h
const container = document.querySelector('#container')
const vnode = h('ul#list', {}, [ h('li.item', {}, 'Item 1'), h('li.item', {}, 'Item 2'), ]) patch(container, vnode)
document.querySelector('#btn-change').addEventListener('click', () => { const newNode = h('ul#list', {}, [ h('li.item', {}, 'Item 1'), h('li.item', {}, 'Item 3'), h('li.item', {}, 'Item 2'), ]) patch(vnode, newNode) }) </script> </body> </html>
|
我们需要观察一下点击按钮后 dom 结构的变化:
只有下面两个 li 标签出现了闪动,说明只有下面两个标签进行了重新渲染,而第一个 li 标签并未重新渲染!从这里我们可以看出 vdom 的优势:它会将新旧 vnode 进行对比,得到最小的更新范围,再更新 DOM 。只有这样,我们才能在数据驱动视图的模式下,有效地控制 DOM 操作。
vdom总结:
- 使用 JS 模拟 DOM 结构(vnode)
- 进行新旧 vnode 对比,得出最小的更新范围,最后更新 DOM
- 在数据驱动视图的模式下,可以有效控制 DOM 操作