1

DOM树和虚拟DOM树

 2 years ago
source link: https://lianpf.github.io/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%8E%9F%E7%90%86/dom%E6%A0%91%E5%92%8C%E8%99%9A%E6%8B%9Fdom%E6%A0%91/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

渲染流水线中,DOM树本身的缺陷很容易引起重排、重绘等性能问题,后来有了虚拟DOM的…


一、DOM树

1、聊一下DOM

网络进程获取到的html字节流无法直接被渲染进程理解,需要转化成渲染引擎可以理解的结构。即DOM。有以下作用:

  • 表述HTML的内部数据结构
  • 将Web⻚面和JavaScript脚本连接起来
  • DOM解析阶段即过滤一些不安全的内容

渲染引擎内部通过HTML解析器(HTMLParser)将HTML字节流转化为DOM结构

2、DOM的具体生成流程:

网络进程和渲染进程建立一个流式管道,HTML解析器直接解析, 不需要等待text/html类型的接口接受完毕再进行解析

字节流转换为DOM
图:字节流转换为DOM

  • 第一个阶段,通过分词器将字节流转换为Token
  • 第二个和第三个阶段同步进行,需要将Token解析为DOM节点,并将DOM节点添加到DOM 树中

HTML解析器开始工作时,会默认创建了一个根为document的空DOM结构。同时会将一个StartTag document的Token压入栈底。然后经过分词器解析出来的第一个StartTag html Token会 被压入到栈中,并创建一个html的DOM节点,添加到document上

解析到StartTag html时的状态
图:解析到StartTag html时的状态

解析出第一个文本Token时的状态
图:解析出第一个文本Token时的状态

元素弹出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执行流程
图:结合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模型
图:基于React和Redux构建MVC模型

  • 控制器用来监控DOM的变化,一旦DOM发生变化,控制器便会通知模型,让其更新数据
  • 模型数据更新后,控制器通知视图,告诉它模型的数据发生了变化
  • 视图接收到更新消息后,根据模型所提供的数据来生成新的虚拟DOM
  • 新的虚拟DOM生成好后,与之前的虚拟DOM进行比较,找出变化的节点
  • 比较出变化的节点后,React将变化的虚拟节点应用到DOM上,触发DOM节点更新
  • DOM节点的变化触发后续一系列渲染流水线变化,从而实现⻚面的更新

最后, 希望大家早日实现:成为前端高手的伟大梦想!
欢迎交流~

微信公众号

本文版权归原作者曜灵所有!未经允许,严禁转载!对非法转载者, 原作者保留采用法律手段追究的权利!
若需转载,请联系微信公众号:连先生有猫病,可获取作者联系方式!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK