渲染流水线中,DOM树本身的缺陷很容易引起重排、重绘等性能问题,后来有了虚拟DOM的…
一、DOM树
1、聊一下DOM
网络进程获取到的html字节流无法直接被渲染进程理解,需要转化成渲染引擎可以理解的结构。即DOM。有以下作用:
- 表述HTML的内部数据结构
- 将Web⻚面和JavaScript脚本连接起来
- DOM解析阶段即过滤一些不安全的内容
渲染引擎内部通过HTML解析器(HTMLParser)将HTML字节流转化为DOM结构
2、DOM的具体生成流程:
网络进程和渲染进程建立一个流式管道,HTML解析器直接解析, 不需要等待text/html类型的接口接受完毕再进行解析
图:字节流转换为DOM
- 第一个阶段,通过分词器将字节流转换为Token
- 第二个和第三个阶段同步进行,需要将Token解析为DOM节点,并将DOM节点添加到DOM 树中
HTML解析器开始工作时,会默认创建了一个根为document的空DOM结构。同时会将一个StartTag document
的Token压入栈底。然后经过分词器解析出来的第一个StartTag html Token会 被压入到栈中,并创建一个html的DOM节点,添加到document上
图:解析到StartTag html时的状态
图:解析出第一个文本Token时的状态
图:元素弹出Token栈示意图
3.DOM缺陷:DOM树构建被JS和CSS文件影响
JavaScript文件的下载过程会阻塞DOM解析
预解析操作:如果JavaScript文件中没有操作 DOM相关代码,就可以将该JavaScript脚本设置为异步加载,通过async 或defer来标记代码
<script async type="text/javascript" src='foo.js'></script>
JavaScript会阻塞DOM生成,而样式文件又会阻塞JavaScript的执行
二、虚拟DOM:虚拟DOM和实际的DOM有何不同?
Javascript直接操作DOM可能会引起重排、重绘等操作(强制同步布局和布局抖 动)引起性能问题。虚拟DOM作为中间层来优化dom的操作(批量更新dom,优化更新dom细节)。之后从双缓存和MVC模型的⻆度来解析了虚拟DOM
频繁DOM操作非常消耗浏览器性的,虚拟DOM核心是将批量DOM操作后的变化一次性更新到浏览器
1、DOM缺陷及虚拟DOM是如何解决的
1.DOM的一些缺陷
- 通过 JavaScript操纵DOM是会影响到整个渲染流水线的
- 使用DOM提供的JavaScript接口来遍历或者修改节点
以上两种操作都会引起重排、重绘或合成操作“牵一发而动全身”。另外,DOM中不当操作还可能引发强制同步布局和布局抖动问题,大大降低渲染效率。
2.虚拟DOM是如何解决这些缺陷
- ⻚面改变的内容应用到虚拟DOM上,不是直接应用到DOM上
- 变化应用到虚拟DOM上时,虚拟DOM并不急着去渲染⻚面,而是调整虚拟DOM的内部状态
- 在虚拟DOM收集到足够的改变时,把这些变化一次性应用到真实的DOM上
图:结合React流程虚拟DOM执行流程
- 创建阶段:依据JSX和基础数据创建出来虚拟DOM,由虚拟DOM树创建出真实DOM树,再触发渲染流水线输出⻚面
- 更新阶段:数据变更,根据新数据创建新的虚拟DOM树,结合
React Fiber更新机制
找出变化的地方,把变化的地方一次性更新到真实的DOM树上,触发渲染流水线
比较两个虚拟DOM的过程是在一个递归函数里执行的,其 核心算法是reconciliation。通常情况下,这个比较过程执行得很快,不过当虚拟DOM比较复杂时,执行比较函数可能占据主线程比较久的时间,导致其他任务的等待,造成⻚面卡顿。为了解决这个问题,React团队重写了reconciliation算法,新的算法称为Fiber reconciler,之前老的算法称为Stack reconciler。协程的别称就是Fiber,所谓的Fiber reconciler就是在执行算法的过程中出让主线程
2、在双缓存和MVC的视⻆来聊聊虚拟DOM
(1)双缓存
图像操作复杂的页面中,完整的画面需要多次计算完成。所以,当屏幕从前缓冲区读取数据显示的时候,可能拿到的是只计算了一部分的图像,就会造成用户看到的图像是一部分一部分显示出来的,也就是页面的闪烁。
使用双缓存,计算的中间结果存放在另一个缓冲区中,等全部的计算结束,该缓冲区已经存储了完整的图形之后,再将该缓冲区的图形数据一次性复制到显示缓冲区,这样就使得整个图像的输出非常稳定
在这里,你可以把虚拟DOM看成是DOM的一个buffer,和图形显示一样,它会在完成一次完整的操作之 后,再把结果应用到DOM上,这样就能减少一些不必要的更新,同时还能保证DOM的稳定输出
(2)MVC模式
可以把React中虚拟DOM的部分看成是一个MVC中的视图,结合Redux提供的控制器和模型构建一个MVC的模型结构,如下图所示:
图:基于React和Redux构建MVC模型
- 控制器用来监控DOM的变化,一旦DOM发生变化,控制器便会通知模型,让其更新数据
- 模型数据更新后,控制器通知视图,告诉它模型的数据发生了变化
- 视图接收到更新消息后,根据模型所提供的数据来生成新的虚拟DOM
- 新的虚拟DOM生成好后,与之前的虚拟DOM进行比较,找出变化的节点
- 比较出变化的节点后,React将变化的虚拟节点应用到DOM上,触发DOM节点更新
- DOM节点的变化触发后续一系列渲染流水线变化,从而实现⻚面的更新
最后, 希望大家早日实现:成为编程高手的伟大梦想!
欢迎交流~
本文版权归原作者曜灵所有!未经允许,严禁转载!对非法转载者, 原作者保留采用法律手段追究的权利!
若需转载,请联系微信公众号:连先生有猫病,可获取作者联系方式!