项目作者: Hanqing1996

项目描述 :
对虚拟 DOM 的理解
高级语言:
项目地址: git://github.com/Hanqing1996/virtual-DOM.git
创建时间: 2020-10-07T06:36:07Z
项目社区:https://github.com/Hanqing1996/virtual-DOM

开源协议:

下载


参考:

在状态(数据)改变后如何更新视图

  1. DOM 操作

    每次修改状态后操作相关的DOM

  2. MVVM

    让视图和状态进行绑定,状态变更了视图自动变更,即双向数据绑定。 vue 借鉴了 MVVM 的设计模式,采用数据劫持+发布订阅模式实现数据绑定。

  3. virtual DOM

    一旦状态发生了变化,就用模版引擎重新渲染整个视图,然后用新的视图更换掉旧的视图。


Virtual DOM 算法

  1. 用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
  2. 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
  3. 把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了

Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然操作 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)


批量接口优于多次调用单个接口

从总的时间成本来说,批量接口都是比多次调用单个接口的总和要节省的

东风吹,战鼓擂,厂里来了新同事,车间主任召集大家开个会:今天我们车间新来了关羽同志,来自河东解良,大家欢迎,然后就是各种套话,一不小心15分钟过去了,大家都聚在一起,抓了革命没促生产。

明天又来了张飞同志,后天来了赵云,然后马超黄忠魏延,每来一个人,有很多事情都得来这么一遍,如果这群人是同一天来,一次也就30分钟就都介绍完了,每天来一个,总的就花了一个多小时,所以,从总的时间成本来说,批量接口都是比多次调用单个接口的总和要节省的,因为可以有一些内部优化。

比如说,你有一个数组,被展示成了一个列表,之前有5个数组项:

[0, 1, 2, 3, 4]

展示成了:

  1. <ul>
  2. <li>0</li>
  3. <li>1</li>
  4. <li>2</li>
  5. <li>3</li>
  6. <li>4</li>
  7. </ul>

现在,给数组新增了10个元素,变成了从0-14这样的状况,思考一下如果要创建li节点,是怎么处理?

最直接的方式,是这样:

  1. for (var i=5; i<15; i++) {
  2. var li = document.createElement("li");
  3. li.innerHTML = arr[i];
  4. ul.appendChild(li);
  5. }

这里其实就有问题,因为性能是不好的,批量操作应当被合并。一般会用一个合并好的html字符串,或者DocumentFragment先做好批量的创建合并,再一次追加到DOM树去。

virtual-DOM 作为架设在 DOM 树和 js 之间的缓存结构,可以批量操作 js 数据,再一次性生成 HTML 结构传递给 DOM 树。

操作 virtual DOM,实际操作的是 js 数据。


batching 和diff

0axOZF.jpg

同样的数据(1000 元素),分20次解析(parseHTML), 每次解析50个,耗时是 一次性解析的 10 倍左右。。也就是说,有9倍开销,都花在了DOM函数,以及背后的依赖的函数本身的开销上了。

这个官方称为”API Overhead”. 改变DOM 结构的调用都有极其高的API Overhead.

而对于Overhead高的API,标准解决办法就是两个:Batching 和 Diff.注意这两个办法的核心

  • Batching

    思路:尽量将多次的 DOM 节点操作合并到一次。比如将”解析生成1000个元素,分别插入DOM树,共计有1000次DOM操作”改为“将含有1000个元素的HTML字符串,解析后一次性追加到DOM树中,共计只有一次DOM操作”。也就是前面批量接口优于多次调用单个接口的思路。

类似技术在游戏引擎里面(对 OPENGL 的Batch), 网络传输方面( 报文的Batch), 文件系统读写上都大量有使用。

  • diff

    思路:减少不必要的DOM操作。比如一个 table 中的一行内容被改变,那么没必要对其余未发送变化的DOM节点进行操作。


virtual DOM 为函数式的 UI 编程方式打开了大门

这是一个性能 vs. 可维护性的取舍。框架的意义在于为你掩盖底层的 DOM 操作,让你用更声明式的方式来描述你的目的,从而让你的代码更容易维护。没有任何框架可以比纯手动的优化 DOM 操作更快,因为框架的 DOM 操作层需要应对任何上层 API 可能产生的操作,它的实现必须是普适的。针对任何一个 benchmark,我都可以写出比任何框架更快的手动优化,但是那有什么意义呢?在构建一个实际应用的时候,你难道为每一个地方都去做手动优化吗?出于可维护性的考虑,这显然不可能。框架给你的保证是,你在不需要手动优化的情况下,我依然可以给你提供过得去的性能。

作者:尤雨溪
链接:https://www.zhihu.com/question/31809713/answer/53544875
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


virtual DOM 具有跨平台性

可以渲染到 DOM 以外的 backend,比如 ReactNative。


virtual DOM 双缓冲

在开发游戏或者处理其他图像的过程中,屏幕从前缓冲区读取数据然后显示。但是很多图形操作都很复杂且需要大量的运算,比如一幅完整的画面,可能需要计算多次才能完成,如果每次计算完一部分图像,就将其写入缓冲区,那么就会造成一个后果,那就是在显示一个稍微复杂点的图像的过程中,你看到的页面效果可能是一部分一部分地显示出来,因此在刷新页面的过程中,会让用户感受到界面的闪烁。

而使用双缓存,可以让你先将计算的中间结果存放在另一个缓冲区中,等全部的计算结束,该缓冲区已经存储了完整的图形之后,再将该缓冲区的图形数据一次性复制到显示缓冲区,这样就使得整个图像的输出非常稳定

可以把虚拟 DOM 看成是 DOM 的一个 buffer,和图形显示一样,它会在完成一次完整的操作之后,再把结果应用到 DOM 上,这样就能减少一些不必要的更新,同时还能保证 DOM 的稳定输出。

Vue采用虚拟DOM的目的

  1. Vue 2.0 引入 vdom 的主要原因是 vdom 把渲染过程抽象化了,从而使得组件的抽象能力也得到提升,并且可以适配 DOM 以外的渲染目标。
  2. 不再依赖 HTML 解析器进行模版解析,可以进行更多的 AOT 工作提高运行时效率:通过模版 AOT 编译,Vue 的运行时体积可以进一步压缩,运行时效率可以进一步提升;
  3. 可以渲染到 DOM 以外的平台,实现 SSR、同构渲染这些高级特性,Weex 等框架应用的就是这一特性。