为什么会有虚拟 DOM?

DOM 结构复杂时,每次操作 DOM 渲染引擎都需要进行重排、重绘或者合成操作,非常耗时,耗性能。需要一种方式来减少 JS 对 DOM 的操作,于是就有了虚拟 DOM。

虚拟 DOM 要解决的事情

  1. 将页面改变的内容应用到虚拟 DOM 上,而不是真实 DOM
  2. 虚拟 DOM 不立马渲染页面,而仅仅是调整虚拟 DOM 内部状态
  3. 在虚拟 DOM 收集到足够的改变时,再把这些变化一次性应用到真实 DOM

什么是虚拟 DOM?

下图为虚拟 DOM 的执行流程
虚拟 DOM 执行流程.png

创建阶段:依据数据创建出虚拟 DOM,然后由虚拟 DOM 创建出真实 DOM,生成完后触发渲染流水线输出到页面。

更新阶段:数据发生变化,根据新数据创建一个新的虚拟 DOM 树,然后与旧的比较,找出变化的地方,并把变化的地方一次性更新到真实的 DOM 树上,最后渲染引擎更新渲染流水线,生成新页面。

React Fiber 更新机制

React 最初比较虚拟 DOM 的过程是在递归函数里,核心算法是 reconciliation. 正常情况下很快,虚拟 DOM 结构一复杂时,执行比较函数会占据主线程比较久的时间,就会导致其他任务等待,造成页面卡顿。于是发明了新算法Fiber reconciler.

Fiber 的另一个称呼就是协程,在执行算法过程中让出主线程,就解决了以前Stack reconciler函数占用时间过久的问题。

双缓存与 MVC 视角下的虚拟 DOM

  1. 双缓存

当渲染复杂图像时,需要计算很多次才能完成,如果每计算一点就写入缓冲区,看到的页面效果就是一点一点显示出来,会让用户感受到界面闪烁。

使用双缓存,可以把计算机的中间结果存放在另一个缓冲区,等全部计算结束,缓冲区存储完整图形之后,再将该缓冲区的图形数据一次性复制到显示缓冲区,这样图像就会稳定输出。

虚拟 DOM 的 DOM 就相当于一个 buffer,它会在一次完整操作后将结果应用到 DOM 上,即减少了不必要的更新,还能保证 DOM 的稳定输出。

双缓存是一种经典的思路,应用在很多场合,能解决页面无效刷新和闪屏问题。

  1. MVC 模式

基于 React 和 Redux 的 MVC 模型.png
图中的控制器是用来监控 DOM 的变化,一旦 DOM 发生变化,控制器便通知模型,让其更新数据。

模型数据更新好之后,控制器会通知视图,告诉他模型数据发生变化。

视图收到数据后,根据数据生成新的虚拟 DOM。然后与之前虚拟 DOM 对比,找出变化节点。

然后将变化节点用到 DOM 上,触发 DOM 节点的更新。DOM 节点的更新又会触发后续一系列渲染流水线的变化,实现页面更新。