对虚拟 DOM 的理解
参考:
在状态(数据)改变后如何更新视图
DOM 操作
每次修改状态后操作相关的DOM
MVVM
让视图和状态进行绑定,状态变更了视图自动变更,即双向数据绑定。 vue 借鉴了 MVVM 的设计模式,采用数据劫持+发布订阅模式实现数据绑定。
virtual DOM
一旦状态发生了变化,就用模版引擎重新渲染整个视图,然后用新的视图更换掉旧的视图。
Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然操作 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)
从总的时间成本来说,批量接口都是比多次调用单个接口的总和要节省的
东风吹,战鼓擂,厂里来了新同事,车间主任召集大家开个会:今天我们车间新来了关羽同志,来自河东解良,大家欢迎,然后就是各种套话,一不小心15分钟过去了,大家都聚在一起,抓了革命没促生产。
明天又来了张飞同志,后天来了赵云,然后马超黄忠魏延,每来一个人,有很多事情都得来这么一遍,如果这群人是同一天来,一次也就30分钟就都介绍完了,每天来一个,总的就花了一个多小时,所以,从总的时间成本来说,批量接口都是比多次调用单个接口的总和要节省的,因为可以有一些内部优化。
比如说,你有一个数组,被展示成了一个列表,之前有5个数组项:
[0, 1, 2, 3, 4]
展示成了:
<ul>
<li>0</li>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
现在,给数组新增了10个元素,变成了从0-14这样的状况,思考一下如果要创建li节点,是怎么处理?
最直接的方式,是这样:
for (var i=5; i<15; i++) {
var li = document.createElement("li");
li.innerHTML = arr[i];
ul.appendChild(li);
}
这里其实就有问题,因为性能是不好的,批量操作应当被合并。一般会用一个合并好的html字符串,或者DocumentFragment先做好批量的创建合并,再一次追加到DOM树去。
virtual-DOM 作为架设在 DOM 树和 js 之间的缓存结构,可以批量操作 js 数据,再一次性生成 HTML 结构传递给 DOM 树。
操作 virtual DOM,实际操作的是 js 数据。
同样的数据(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节点进行操作。
这是一个性能 vs. 可维护性的取舍。框架的意义在于为你掩盖底层的 DOM 操作,让你用更声明式的方式来描述你的目的,从而让你的代码更容易维护。没有任何框架可以比纯手动的优化 DOM 操作更快,因为框架的 DOM 操作层需要应对任何上层 API 可能产生的操作,它的实现必须是普适的。针对任何一个 benchmark,我都可以写出比任何框架更快的手动优化,但是那有什么意义呢?在构建一个实际应用的时候,你难道为每一个地方都去做手动优化吗?出于可维护性的考虑,这显然不可能。框架给你的保证是,你在不需要手动优化的情况下,我依然可以给你提供过得去的性能。
作者:尤雨溪
链接:https://www.zhihu.com/question/31809713/answer/53544875
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
可以渲染到 DOM 以外的 backend,比如 ReactNative。
在开发游戏或者处理其他图像的过程中,屏幕从前缓冲区读取数据然后显示。但是很多图形操作都很复杂且需要大量的运算,比如一幅完整的画面,可能需要计算多次才能完成,如果每次计算完一部分图像,就将其写入缓冲区,那么就会造成一个后果,那就是在显示一个稍微复杂点的图像的过程中,你看到的页面效果可能是一部分一部分地显示出来,因此在刷新页面的过程中,会让用户感受到界面的闪烁。
而使用双缓存,可以让你先将计算的中间结果存放在另一个缓冲区中,等全部的计算结束,该缓冲区已经存储了完整的图形之后,再将该缓冲区的图形数据一次性复制到显示缓冲区,这样就使得整个图像的输出非常稳定