本文主要是自己对https://www.zhihu.com/question/31809713一贴中各位大神分享的学习记录,不一定对,希望通过不断学习加深理解。
问题:为什么需要virtual dom?
先的说说virtual dom 是什么,virtual dom就是原生dom在内存中的js对象映射,但不是完全复制,它比真实dom节点在属性上简化了,也就是说是更加轻量级,相当于是数据和原生dom的一层缓存。
在司徒正美的回复中有例子。这里就不罗列了。
之前一直有一种说法,说是virtual dom操作比原生dom快。
先说原生dom,dom发生变化后,就需要重新布局,绘制,渲染等等,一般说的原生dom慢,主要是说重新渲染,绘制的消耗。
对于初始渲染,其实没有大的差别,毕竟virtual dom最终也是要渲染成原生dom的。主要的差异体现在后续的变更上。
我们可以比较一下 innerHTML vs. Virtual DOM 的重绘性能消耗:
innerHTML: render html string O(template size) + 重新创建所有 DOM 元素
O(DOM size)
Virtual DOM: render Virtual DOM + diff O(template size) + 必要的 DOM 更新 O(DOM change)。
但是如上面讲到的,主要的消耗在dom的重绘,渲染,相比之下js diff的计算,虽然会耗一部分时间,但是其整体占比不多,因此也还不错。像react还提供了shouldComponentUpdate用来优化这种场景,因此可以说使用了Virtual DOM,在大部分场景下性能还是可以接受的。
“可以看到,innerHTML 的总计算量不管是 js 计算还是 DOM 操作都是和整个界 面的大小相关,但 Virtual DOM 的计算量里面,只有 js 计算和界面大小相关,DOM 操作是和数据的变动量相关的。前面说了,和 DOM 操作比起来,js 计算是极其便宜的。这才是为什么要有 Virtual DOM:它保证了 1)不管你的数据变化多少,每次重绘的性能都可以接受;2) 你依然可以用类似 innerHTML 的思路去写你的应用”
Virtual DOM和MVVM
相比起 React,其他 MVVM 系框架比如 Angular, Knockout 以及 Vue、Avalon 采用的都是数据绑定:通过 Directive/Binding 对象,观察数据变化并保留对实际 DOM 元素的引用,当有数据变化时进行对应的操作。MVVM 的变化检查是数据层面的,而 React 的检查是 DOM 结构层面的。
MVVM 实现的一个共同问题就是在列表渲染的数据源变动时,尤其是当数据是全新的对象时,如何有效地复用已经创建的 ViewModel 实例和 DOM 元素。假如没有任何复用方面的优化,由于数据是 “全新” 的,MVVM 实际上需要销毁之前的所有实例,重新创建所有实例,最后再进行一次渲染!这就是为什么题目里链接的 angular/knockout 实现都相对比较慢。相比之下,React 的变动检查由于是 DOM 结构层面的,即使是全新的数据,只要最后渲染结果没变,那么就不需要做无用功。 Angular 和 Vue 用了 track by $index 用来优化这种场景。
性能
Virtual DOM从来都不是单独存在的。Virtual DOM、脏检查 MVVM、数据收集 MVVM 在不同场合各有不同的表现和不同的优化需求。Virtual DOM 为了提升小量数据更新时的性能,也需要针对性的优化,比如 shouldComponentUpdate 或是 immutable data。
初始渲染:Virtual DOM > 脏检查 >= 依赖收集
小量数据更新:依赖收集 >> Virtual DOM + 优化 > 脏检查(无法优化) > Virtual DOM无优化
大量数据更新:脏检查 + 优化 >= 依赖收集 + 优化 > Virtual DOM(无法/无需优化)>> MVVM 无优化。
其实如果一定要说这个为什么? 那就是Virtaul DOM是作为一定场景下,解决性能问题提出来的解决方案。
他山之石
虚拟DOM快在哪里
然后,js计算肯定要比DOM操作快啊,每次DOM操作都很有可能引起回流(Reflow)和重绘(Repaint)啊。当然浏览器也不傻,不是你每次操作DOM浏览器都重绘一次,一般浏览器会按照时间或次数间隔进行DOM操作的批处理。那问题来了,到底是浏览器优化后的DOM批处理快,还是React的虚拟DOM+优化diff算法快。这个我没有测试过,根据博客上内容,React的优化更人性化、也更快。速度快肯定是虚拟DOM的一个优点,另外一点,浏览器对DOM操作批处理的主动权不在前端人员手中,React将这种批处理的时机选择交到了我们手中,看我们什么时候想render页面。这个和AJAX+回调带来的问题一样,当我们发送一个AJAX,然后指定一个回调函数时候,对回调里代码的控制权就交给浏览器了,所以前端人员常常感觉AJAX不可控,不知道回调什么时候执行完了,页面一大堆flag。
https://blog.csdn.net/qiqingjin/article/details/51804138