虚拟DOM旨在解决浏览器的性能问题。如果一个操作中有10次DOM更新,虚拟DOM不会立即对DOM进行操作,而是将这10次更新的差异(diff)内容保存到一个本地JS对象中,最后将这个JS对象附加到DOM树一次,以便后续操作,这样可以避免大量不必要的计算。因此,使用JS对象模拟DOM节点的好处是,页面的更新可以先全部反映在JS对象(虚拟DOM)中,在内存中操作JS对象的速度明显更快。更新完成后,最终的JS对象将被映射到真正的DOM中,由浏览器绘制。
1. 通常,我们在操作一个 DOM 结构的时候会有哪几种方式?
(1) 通过原生 js,精确地选中我们要操作的 DOM 节点,然后对其进行相应的操作。
比如我们要修改一个 div 节点的内容,就需要这样做
const div = document.getElementById('div')
div.innerText = 'hello world'
复制代码
这样我们就把 div 这个元素中的内容修改为 hello world。
(2) 第二种方式是通过一个 html 模板字符串去创建一个节点,替换当前节点,这种方式在 jQuery 时代是非常常用的。
const htmlTemplate = `<div>hello world</div>`
wrap.innerHTML = htmlTemplate
这样也能达到同样的目的。
(3) 第三种就是通过虚拟 DOM 的方式进行修改了
const virtualDOM = {
tag: 'div',
children: [{ children: 'hello world' }]
}
对比以上三种方法,你觉得哪种方法的性能最好呢?
很显然是第一种,选中要修改的元素,直接修改其内容。
其次应该是第三种方法,为什么不是第二种呢?
因为在通过虚拟 DOM 修改 div 的内容的时候,可以通过 diff 算法,来最小化的修改变更的内容。对于上边的案例来说,只是 div 的内容发生了变化,而 div 元素本身并没有修改任何东西,所以对于通过虚拟 DOM 修改 div 内容的时候,div 标签不会被重新创建。而使用 html 模板字符串的方法,则会对模板中的所有节点进行重新创建。
理论上,第一种方法的性能已经是最好的了,因为没有任何其他额外的操作去消耗性能了。
相比于第一种方法,虚拟 DOM 多做的一件事是虚拟 DOM 对象的创建以及修改目标的查找。
但是呢,这个过程是纯 javascript 层面的计算,所以虽然两种方式有的性能上会有差异,但是并不会差别很大。
毕竟相较于 DOM 层面的计算,JavaScript 层面计算带来的性能差异几乎可以忽略不计了。
2. Vue为什么使用虚拟dom
虚拟DOM就是为了解决直接操作DOM导致的性能问题而被设计出来的。
使用了虚拟DOM之后,假如一次操作中有10次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内容保存到本地的一个js对象中,最终将这个js对象一次性attach到DOM树上,通知浏览器去执行绘制工作,这样可以避免大量的无用的计算量。
Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个内存。既然 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个虚拟DOM。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)。
Virtual DOM的优势不在于单次的操作,而是在大量、频繁的数据更新下,能够对视图进行合理、高效的更新。
3. 虚拟dom的优缺点
(1)优点:
保证性能下限: 虚拟DOM可以经过diff找出最小差异,然后批量进行patch,这种操作虽然比不上手动优化,但是比起粗暴的DOM操作性能要好很多,因此虚拟DOM可以保证性能下限
无需手动操作DOM: 虚拟DOM的diff和patch都是在一次更新中自动进行的,我们无需手动操作DOM,极大提高开发效率
跨平台: 虚拟DOM本质上是JavaScript对象,而DOM与平台强相关,相比之下虚拟DOM可以进行更方便地跨平台操作,例如服务器渲染、移动端开发等等
(2)缺点:
无法进行极致优化: 在一些性能要求极高的应用中虚拟DOM无法进行针对性的极致优化,比如VScode采用直接手动操作DOM的方式进行极端的性能优化