你可能对virtual-dom存在误解

下面这段代码,会产生什么样的结果?

1
2
3
4
5
6
7
function sleep(time) {
const start = Date.now()
while (Date.now() - start < time * 1000) {}
}

document.body.style.background = 'black'
sleep(5)

再来

1
2
3
4
document.body.style.background = 'blue'
sleep(2)
document.body.style.background = 'red'
sleep(3)

异步吗?batch更新吗?
更改了dom对象,浏览器也并不会立刻立马去渲染,那么,js所能触及到的dom对象,在浏览器层面,是否也是virtual-dom呢?
给浏览器写代码的人各各都是人才,这种性能优化肯定是会做的。

什么是virtual-dom

网页是什么,我认为是信息的载体,而html + css生成的dom,就是一种结构化表达信息的数据结构,是浏览器对信息的抽象。

那么这种抽象只有dom一种吗?当然不是,它可以有无数种,只要能够完整的表达结构化的信息就行,它可以跟dom结构类似,是一棵树,比如传统的virtual-dom实现;理论上它也可以是其它的数据结构。

所以,我认为一个可以完整描述信息的对象,就是virtual-dom

为什么需要virtual-dom

是react将virtual-dom这个概念带进了我的视野,所以我尝试从react的角度来看为什么需要virtual-dom。

1
view = f(state)

从函数式编程的角度来讲,不同的state经过渲染函数,就会产生不同的UI,所以理想状况下,状态变了,重新渲染一个,整体替换掉就ok了。

然而,理想很丰满,现实却是dom操作性能不佳,简单粗暴的全量更新会带来明显的性能问题。解决这个问题,很容易的想法是维护状态–>追踪变化–>部分更新。

这样的思路没有问题,angular和vue 2.x之前的版本都是这样做的,核心思路就是细粒度的绑定:每一部分状态对应一部分UI,当这一部分状态变了,通过绑定关系找到对应的ui,进行相应的更新。

但是这样的思路很不函数式,和react的基本思路背道而驰,而且这个过程,会让本来清晰明了的渲染过程变得复杂,需要引入watcher, dependency tracking,脏检查,建立绑定等等。

而计算机领域的所有问题,都可以通过引入一个中间层来解决,通过引入virtual-dom,保持了view = f(state) 的纯粹,view变成了virtual-dom。当状态变化的时候,不从追踪状态变化–>响应变化这个角度去考虑问题,而从diff最终渲染结果这个角度来思考。

关于virtual-dom的性能谣言

似乎提及virtual-dom,很多人的印象就是性能高效,什么O(n)的diff啦,batch修改啦,dom操作慢啦等等。但是,virtual-dom的性能并不突出,尤其是react的实现。

不管采用什么样的手段,最终还是得调用dom api,而且最终进行的dom操作只会更多,不会更少,原生的总是更快,这毫无疑问。

react从没有宣传过自己是一个高性能的框架,因为它的性能确实并不突出,拿它和我熟悉的Vue做个比较:

  • 初始渲染:virtual-dom胜

因为vue需要对对象进行「改造」,设置getter,setter,watcher,收集依赖,这些都有开销

  • 数据更新:vue > virtual-dom

vue通过依赖收集,能够「精确定位」,进行局部更新,非常高效,就像精确制导导弹。

而virtual-dom需要重新渲染整个组件,然后同步遍历新旧、组件进行diff,然后拿到diff的结果进行patch,patch的过程中通常也需要对dom进行遍历。这个过程像是扫描战场,发现目标。

特别是当小数据量更新的时候,vue的依赖收集性能优势非常明显。更不用说react还有无法解决的跨层级移动,和解决的很不好的列表顺序变化的性能问题。

所以vue早期打出了「高性能」的旗号,确实是在大部分场景下,性能都优于react,不过依赖收集也不是免费的,所以初始渲染会比react慢。

但是,virtual-dom确实解决了react状态更新的渲染性能问题,在坚持view = f(state)这个条件下,做到了局部更新

virtual-dom的意义

尤雨溪大大的看法我觉得非常正确,搬运如下:

在我看来 Virtual DOM 真正的价值从来都不是性能,而是它 1) 为函数式的 UI 编程方式打开了大门;2) 可以渲染到 DOM 以外的 backend,比如 ReactNative

我想第二点也是vue引入virtual dom的理由吧。