Vue 为什么要用虚拟 DOM(Virtual DOM)

Vue 为什么要用虚拟 DOM(Virtual DOM)

背景

依旧接着说面试那点事,最近面试了很多人,面对曾经用过 vue 的候选人,我都会问一个问题:Vue 为什么要在 2.x 之后切换到 虚拟 DOM 上?
而在这个过程中我发现了很多人都会陷入一个误区:** 选择一个技术一定是因为这个技术是好的、优秀的,所以才会用它。** 这个理解实际上也并不算错,但是我认为也是不妥的,很多时候选择一个技术真不是因为它多好,而是因为它能解决你当前碰到的问题,所以哪怕它并不完美,甚至并不好,可是只要能解决问题,并且暂时并没有其它更好的解决方案,同时带来的后果能在承受范围之内,那么就可以采用它。很显然,虚拟 DOM 就是一个这样的东西。


虚拟 DOM 真的快吗?

非常多的人在说到虚拟 DOM 都会说虚拟 DOM 的 diff 算法如何如何,怎么提升性能如何如何,但事实上只要仔细思考一个问题你就能知道这个结论是站不住脚的,至少在相当多情况下是站不住脚的:虚拟 DOM 通过 diff 算法找到了发生变化后的节点,它是不是依旧要操作 真实 DOM 修改?那么它凭什么就比直接修改 真实 DOM 更快呢?(显然,它多了一层 diff 操作)。
当然很多人会提到,虚拟 DOM 可以批量在内存操作,然后把结果输出到浏览器,大量修改的情况下性能更优秀,可以保证大量操作下的性能下限,真实操作 DOM 每次都会引起重绘,不如 虚拟 DOM 批量替换效率高。看起来确实是这样,可是事实上呢?首先,并不会有人无聊到真的重复更新大批量数据吧?为了一个极少出现的场景放弃了大多数的场景,值得么?相反,大多数正常操作下,本来只需要修改两个简单的 DOM,而 虚拟 DOM 还要再完整走一遍 diff 算法,才找到这两个节点,再去替换,这是否又拉低了性能呢?更何况 虚拟 DOM 一直有一个解决不了的问题就是:他根本不知道什么时候该更新,什么时候不该更新,也是因为这个原因,React 才提供了 ShouldComponentUpdate 让用户自己控制是否要更新。更更何况,虚拟 DOM 做的这个合并操作,其实手动也是可以操作的,我想做 Canvas 的人应该大有体会,在内存里生成一个虚拟的 Canvas,然后进行多次绘制,之后一次性将这个 Canvas 渲染到页面上。事实上,React 官方并不是很在意这个性能,比如 React 自己的 虚拟 DOM 算法都不是最优秀的(虚拟 DOM 的算法相当多),随便列举几个:

其中 inferno 的速度更是快得离谱。
这足以说明,React 其实是用 虚拟 DOM 根本不是因为快,或者换个说法,虚拟 DOM 的性能并不是 React 的首要考虑,当然也并不是说完全就不考虑性能,inferno 的作者似乎已经进入 React 团队。更愿意说:虚拟 DOM 只是 React 为了实现目的而选择的一条最合适的路,他只是手段,而不是目的,他的 DOM 操作优化也只是避免性能损耗的手段,而不是 React 的根本目的。
当然这并不是说 虚拟 DOM 就没有优点,优点也是很明显的,虽然我们能够手动合并 DOM,可是这毕竟是一个繁琐的操作,如果有框架能帮助我们合并,又何必去手动操作呢?在少量 DOM 更新的情况下,即使 虚拟 DOM 会拖累速度,事实上也并不会对体验有什么影响,反倒是在大量操作的情况下保障了性能下限,会让我们的应用可用性更稳定。

Ok,接着回到 VueVue 事实上是有依赖收集这个过程的,换句话说,其实 Vue 在依赖收集阶段,完全可以精确的收集到页面哪些 DOM 使用了数据,并且可以在数据变化的时候精确的更新这些 DOM,这个性能并不会很差,Vue1 一直是这么干的,而 Vue1 长期标榜自己的性能会比 React 更加优秀也证明了这点,对于 Vue 而言,似乎直接精准收集依赖,精准更新更加的自然,使用虚拟 DOM 看起来更像是脱裤子放屁:我明明可以知道哪个 DOM 更新,依然要装作我不知道,然后走一遍 diff 算法才发现,哦,原来是这几个 dom 更新了。这里提一嘴,AtomVsCode 都是 js 制作的,其中 Atom 曾经使用过 虚拟 DOMAtom 是个什么性能大家都清楚,最终转为 真实 DOM 操作,VsCode 则是一直使用 真实 DOM,编辑器场景其实和 vue 很像,编辑器当然准确知道你操作了哪一行哪一个文字,但是如果他依旧装作不知道要走一遍虚拟 dom,这就正如之前说的,完全是脱裤子放屁的无意义操作。所以不要天真地以为 虚拟 DOM 就是快,就是好,就是一定要上的,虚拟 DOM 并不是完全没有代价的。


虚拟 DOM 是否还有其他优点?

正如之前所说,虚拟 DOM 其实并不见得比手动快,但是他却提供了一个非常重要的特性:可以接受 Parser 解析转化。这意味着其实相当多的东西我们都可以在编译阶段解决,比如:xss 攻击($$typeof)、合并 DOM 操作、跨平台 等等。
其中我认为最重要的就是跨平台,平台不单止 Webios 等,更是止编程语言,比如考虑这样一个情况:我们是否可以编写一套转换器,用来把 python 转换为 js?这其中比较麻烦的就是 DOM,因为 DOM 是 js 独有的东西,可是拥有 虚拟 DOM 之后我们可以在 python 也实现一套,之后转为 虚拟 DOM 这样一个统一的抽象格式,这样不就实现了其他编程语言平台来编写前端代码?事实上 ssr(服务端渲染) 就是这个原理,node 是没有 DOm 的,通过虚拟 DOM 来抽象即可达到操作 DOM 的目的,同时我们在 jsx 里使用函数来编写声明式编程来编写原本命令式的 DOM 操作也是得益于此。再比如我们是否可以把前端平台代码移植到其他平台?比如 React NativeWeex,比如数量繁多的小程序框架。


结论

所以我一直认为,虚拟 DOM 的真实意义不是他的性能,而是他提供了无限的可能,他是对 真实 DOM 的抽象,你可以用一门全新的语言写下代码,抽象为 虚拟 DOM,然后再通过社区其他成员编写完成的 虚拟 DOM 库,来转为各种平台(iosandroid)的页面,只要有一个框架实现这个转换,其他框架都可以享受这个红利。

vue
本作品采用《CC 协议》,转载必须注明作者和本文链接
jinzhuming
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!