浏览器原理 25 # 虚拟DOM和实际的DOM有何不同?
Posted 凯小默
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浏览器原理 25 # 虚拟DOM和实际的DOM有何不同?相关的知识,希望对你有一定的参考价值。
说明
浏览器工作原理与实践专栏学习笔记
DOM 的缺陷
操作 DOM 触发样式计算、布局、绘制、栅格化、合成等任务,还有可能引起重绘或者合成操作。另外,对于 DOM 的不当操作还有可能引发强制同步布局和布局抖动的问题,这些操作都会大大降低渲染效率。
什么是虚拟 DOM
虚拟 DOM 要解决什么事情
- 将页面改变的内容应用到虚拟 DOM 上,而不是直接应用到 DOM 上。
- 变化被应用到虚拟 DOM 上时,虚拟 DOM 并不急着去渲染页面,而仅仅是调整虚拟 DOM 的内部状态,这样操作虚拟 DOM 的代价就变得非常轻了。
- 在虚拟 DOM 收集到足够的改变时,再把这些变化一次性应用到真实的 DOM 上。
虚拟 DOM 执行流程
结合 React 流程画的一张虚拟 DOM 执行流程图
- 创建阶段:首先依据 JSX 和基础数据创建出来虚拟 DOM,它反映了真实的 DOM 树的结构。然后由虚拟 DOM 树创建出真实 DOM 树,真实的 DOM 树生成完后,再触发渲染流水线往屏幕输出页面。
- 更新阶段:如果数据发生了改变,那么就需要根据新的数据创建一个新的虚拟 DOM 树;然后 React 比较两个树,找出变化的地方,并把变化的地方一次性更新到真实的 DOM 树上;最后渲染引擎更新渲染流水线,并生成新的页面。
React Fiber 更新机制
通过上图可以知道,当有数据更新时,React 会生成一个新的虚拟 DOM,然后拿新的虚拟 DOM 和之前的虚拟 DOM 进行比较,这个过程会找出变化的节点,然后再将变化的节点应用到 DOM 上。
重点关注下比较过程,最开始的时候,比较两个虚拟 DOM 的过程是在一个递归函数里执行的,其核心算法是 reconciliation。通常情况下,这个比较过程执行得很快,不过当虚拟 DOM 比较复杂的时候,执行比较函数就有可能占据主线程比较久的时间,这样就会导致其他任务的等待,造成页面卡顿。为了解决这个问题,React 团队重写了 reconciliation 算法,新的算法称为 Fiber reconciler
,之前老的算法称为 Stack reconciler
。
关于协程可以看看之前的笔记:浏览器原理 19 # JavaScript 引擎是如何实现 async / await 以同步的方式来编写异步代码的?
协程的另外一个称呼就是 Fiber
,所以在这里我们可以把 Fiber 和协程关联起来,那么所谓的 Fiber reconciler
就是在执行算法的过程中出让主线程,这样就解决了 Stack reconciler
函数占用时间过久的问题。
双缓存视角看虚拟 DOM
比如:一幅完整的画面,可能需要计算多次才能完成,如果每次计算完一部分图像,就将其写入缓冲区,那么就会造成一个后果,那就是在显示一个稍微复杂点的图像的过程中,你看到的页面效果可能是一部分一部分地显示出来,因此在刷新页面的过程中,会让用户感受到界面的闪烁。
而使用双缓存,可以让你先将计算的中间结果存放在另一个缓冲区中,等全部的计算结束,该缓冲区已经存储了完整的图形之后,再将该缓冲区的图形数据一次性复制到显示缓冲区,这样就使得整个图像的输出非常稳定。
双缓存是一种经典的思路,应用在很多场合,能解决页面无效刷新和闪屏的问题,虚拟 DOM 就是双缓存思想的一种体现。你可以把虚拟 DOM 看成是 DOM 的一个 buffer,和图形显示一样,它会在完成一次完整的操作之后,再把结果应用到 DOM 上,这样就能减少一些不必要的更新,同时还能保证 DOM 的稳定输出。
MVC 模式中虚拟 DOM 所扮演的角色
MVC 是一个非常重要且应用广泛的模式,因为它能将数据和视图进行分离,在涉及到一些复杂的项目时,能够大大减轻项目的耦合度,使得程序易于维护。
MVC 的基础结构:
MVC 的整体结构比较简单,由模型、视图和控制器组成,其核心思想就是将数据和视图分离,也就是说视图和模型之间是不允许直接通信的,它们之间的通信都是通过控制器来完成的。
基于 React 和 Redux 构建 MVC 模型:
在该图中,可以把虚拟 DOM 看成是 MVC 的视图部分,其控制器和模型都是由 Redux 提供的。
其具体实现过程如下:
- 图中的控制器是用来监控 DOM 的变化,一旦 DOM 发生变化,控制器便会通知模型,让其更新数据;
- 模型数据更新好之后,控制器会通知视图,告诉它模型的数据发生了变化;
- 视图接收到更新消息之后,会根据模型所提供的数据来生成新的虚拟 DOM;
- 新的虚拟 DOM 生成好之后,就需要与之前的虚拟 DOM 进行比较,找出变化的节点;
- 比较出变化的节点之后,React 将变化的虚拟节点应用到 DOM 上,这样就会触发 DOM 节点的更新;
- DOM 节点的变化又会触发后续一系列渲染流水线的变化,从而实现页面的更新。
分析基于 React 或者 Vue 这些前端框架时,先重点把握大的 MVC 骨架结构,然后再重点查看通信方式和控制器的具体实现方式。
拓展
其实虚拟 DOM 主要是一个抽象层,用于描述视图,抽象视图变化,诞生主要目的是跨平台。所以才有 React Native,weex。解决 js 操作 dom 性能问题只是附带的,而且虚拟 DOM 是有代价的,在页面结构简单操作也简单的情况下相对 js 操作 DOM 并不一定会有优势。
另外感兴趣的可以看:
可以看一下这篇文章:Virtual Dom库snabbdom代码解析
可以从下面三个方向稍微了解一下 snabbdom
- 第一个是js模拟的dom节点vnode的结构
- 第二个是diff算法
- 第三个是有了diff如何打patch的
以上是关于浏览器原理 25 # 虚拟DOM和实际的DOM有何不同?的主要内容,如果未能解决你的问题,请参考以下文章