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 的算法相当多),随便列举几个:
- GitHub - Matt-Esch/virtual-dom: A Virtual DOM and diffing algorithm
- GitHub - joelrich/citojs
- GitHub - localvoid/kivi: UNMAINTAINED Javascript (TypeScript) library for building web user interfaces
- GitHub - infernojs/inferno: An extremely fast, React-like JavaScript library for building modern user interfaces
其中 inferno 的速度更是快得离谱。
这足以说明,React
其实是用 虚拟 DOM
根本不是因为快,或者换个说法,虚拟 DOM
的性能并不是 React
的首要考虑,当然也并不是说完全就不考虑性能,inferno
的作者似乎已经进入 React 团队。更愿意说:虚拟 DOM 只是 React 为了实现目的而选择的一条最合适的路,他只是手段,而不是目的,他的 DOM 操作优化也只是避免性能损耗的手段,而不是 React 的根本目的。
当然这并不是说 虚拟 DOM
就没有优点,优点也是很明显的,虽然我们能够手动合并 DOM
,可是这毕竟是一个繁琐的操作,如果有框架能帮助我们合并,又何必去手动操作呢?在少量 DOM
更新的情况下,即使 虚拟 DOM
会拖累速度,事实上也并不会对体验有什么影响,反倒是在大量操作的情况下保障了性能下限,会让我们的应用可用性更稳定。
Ok,接着回到 Vue
,Vue
事实上是有依赖收集这个过程的,换句话说,其实 Vue
在依赖收集阶段,完全可以精确的收集到页面哪些 DOM
使用了数据,并且可以在数据变化的时候精确的更新这些 DOM
,这个性能并不会很差,Vue1
一直是这么干的,而 Vue1
长期标榜自己的性能会比 React
更加优秀也证明了这点,对于 Vue
而言,似乎直接精准收集依赖,精准更新更加的自然,使用虚拟 DOM 看起来更像是脱裤子放屁:我明明可以知道哪个 DOM
更新,依然要装作我不知道,然后走一遍 diff 算法才发现,哦,原来是这几个 dom 更新了。这里提一嘴,Atom
和 VsCode
都是 js
制作的,其中 Atom
曾经使用过 虚拟 DOM
,Atom
是个什么性能大家都清楚,最终转为 真实 DOM
操作,VsCode
则是一直使用 真实 DOM
,编辑器场景其实和 vue 很像,编辑器当然准确知道你操作了哪一行哪一个文字,但是如果他依旧装作不知道要走一遍虚拟 dom,这就正如之前说的,完全是脱裤子放屁的无意义操作。所以不要天真地以为 虚拟 DOM
就是快,就是好,就是一定要上的,虚拟 DOM
并不是完全没有代价的。
虚拟 DOM 是否还有其他优点?#
正如之前所说,虚拟 DOM 其实并不见得比手动快,但是他却提供了一个非常重要的特性:可以接受 Parser 解析转化。这意味着其实相当多的东西我们都可以在编译阶段解决,比如:xss 攻击($$typeof)、合并 DOM 操作、跨平台 等等。
其中我认为最重要的就是跨平台,平台不单止 Web
、ios
等,更是止编程语言,比如考虑这样一个情况:我们是否可以编写一套转换器,用来把 python
转换为 js
?这其中比较麻烦的就是 DOM
,因为 DOM
是 js 独有的东西,可是拥有 虚拟 DOM
之后我们可以在 python
也实现一套,之后转为 虚拟 DOM
这样一个统一的抽象格式,这样不就实现了其他编程语言平台来编写前端代码?事实上 ssr(服务端渲染)
就是这个原理,node
是没有 DOm
的,通过虚拟 DOM
来抽象即可达到操作 DOM
的目的,同时我们在 jsx
里使用函数来编写声明式编程来编写原本命令式的 DOM
操作也是得益于此。再比如我们是否可以把前端平台代码移植到其他平台?比如 React Native
、Weex
,比如数量繁多的小程序框架。
结论#
所以我一直认为,虚拟 DOM
的真实意义不是他的性能,而是他提供了无限的可能,他是对 真实 DOM
的抽象,你可以用一门全新的语言写下代码,抽象为 虚拟 DOM
,然后再通过社区其他成员编写完成的 虚拟 DOM
库,来转为各种平台(ios
、android
)的页面,只要有一个框架实现这个转换,其他框架都可以享受这个红利。
本作品采用《CC 协议》,转载必须注明作者和本文链接