React虚拟DOM

首先理解什么是DOM?

DOM(Document Object Model),即文档对象模型

DOM元素是网页的基本元素,大量的DOM元素构成了一个完整的页面。但DOM是很慢的,其元素非常庞大。一般页面的性能问题很少是由JS引起的,大部分都是由DOM操作引起的。

为什么说DOM操作很慢?

因为在访问与修改DOM时开销很大,特别是修改元素时,会导致浏览器再次计算,重新渲染页面。实际上,DOM对象本身也是一个JS对象,所以严格说,并不是操作DOM这个对象慢,而是操作这个对象后,会触发浏览器的一些行为。例如:布局(layout)和绘制(paint)。

浏览器如何呈现一个界面?

一个浏览器有许多模块,其中负责呈现页面的是渲染引擎模块。

浏览器呈现一个页面的大致过程:

  1. 解析HTML,生成一棵DOM tree
  2. 解析各种样式并结合DOM tree生成一棵Render tree
  3. Render tree的各个节点计算布局信息;
  4. 根据Render tree并利用浏览器的UI层进行绘制。

其中DOM tree和Render tree上的节点并非一一对应,Render tree上只存在需要被绘制来显示在页面上的节点,而不显示的节点只有DOM tree存放。

绘制(paint)是一个耗时的过程,布局(layout)是一个更耗时的过程,我们无法确定layout一定是自上而下或是自下而上进行的,甚至一次layout会牵涉到整个文档布局的重新计算。但是layout是肯定无法避免的,所以关键是要最小化layout的次数。

什么情况下浏览器会进行layout?

一般情况下,浏览器的layout是惰性的,在JS脚本执行时,是不会去更新DOM的,任何对DOM的修改都会被暂存在一个队列中,在当前JS上下文完成执行后,会根据这个队列中的修改,进行一次layout。有时希望在js代码中立刻获取最新的DOM节点信息,浏览器就不得不提前执行layout,这是导致DOM性能问题的主因。

如下的操作会打破常规,并触发浏览器执行layout:

  • 通过js获取需要计算的DOM属性

  • 添加或删除DOM元素

  • resize浏览器窗口大小

  • 改变字体

  • CSS伪类的激活(如 :hover)

  • 通过JS修改DOM元素样式且该样式涉及到尺寸的改变

原生中对于减少layout的一些方法

针对一系列DOM操作(DOM元素的增删改),可以有如下方案:

  • documentFragment
  • display: none
  • cloneNode

这类优化方案的核心思想都是相同的:先对一个不在Render tree上的节点进行一系列操作,再把这个节点添加回Render tree,这样无论多么复杂的DOM操作,最终都只会触发一次layout。

使用requestAnimationFrame

任何可能导致重绘的操作都应该放入requestAnimationFrame

项目中,代码按模块划分,很难组织批量读写。这时可以把写操作放在requestAnimationFramecallback中,统一让写操作在下一次paint之前执行。


为什么要用React?

首先,用了requestAnimationFrame后就变成异步编程的问题了。如果要让读写状态同步,那必然需要在DOM的基础上写个Wrapper来内部控制异步读写,不过这是很麻烦的。

react作用就是可以帮忙解决这种问题。因为react中这些优化是默认实施的,不用再去进行那些复杂的操作,且性能很好。前端框架的存在就是为了减轻程序员的工作,让编程更加容易,另一方面使用框架可以让我们的项目更易去维护。

而且,DOM 完全不属于JavaScript (也不在JavaScript 引擎中存在).。JavaScript 其实是一个非常独立的引擎,DOM其实是浏览器引出的一组让JavaScript操作HTML文档的API而已。在即时编译的时代,调用DOM的开销是很大的。而Virtual DOM的执行都在JavaScript 引擎中,完全不会有这个开销。

React.js 相对于直接操作原生DOM有很大的性能优势, 很大程度上都要归功于virtual DOM(虚拟DOM)的batching 和Diff。batching把所有的DOM操作搜集起来,一次性提交给真实的DOM。Diff算法通过对比新旧虚拟DOM树记录之间的差异。

diff算法时间复杂度也从标准的的Diff算法的O(n^3)降到了O(n)。

虚拟DOM的设计思想:

​ (1)提供一种方便的工具,使开发效率得到保证;

​ (2)保证最小化的DOM操作,使得执行效率得到保证。

Virtual DOM 算法。包括几个步骤:

  1. 用 JavaScript 对象结构表示 DOM 树的结构,用这个树构建一个真正的 DOM 树,插到文档当中;
  2. 当状态变更的时候,重新构造一棵新的对象树,用新的树和旧的树进行比较,记录两棵树差异;
  3. 把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。


原生DOM vs virtual DOM

Dom对象一个空的div元素的第一层属性列表:
React虚拟DOM但没有必要让每一个 div 节点都包括 这里的所有属性吧。

与之对应的,以下是 React 中伪 DOM div 元素的第一层属性列表:

props_owner__lifeCycleState__pendingProps__pendingCallbackspendingOwner

可以看出虚拟DOM需要计算的属性相对原生而言会少很多,这样效率就很快,每次操作之后先修改虚拟DOM结构,通过对比之前的状态,计算出最小的DOM修改步骤,再通知真实DOM做最小的修改。虚拟DOM操作最后归根结底还是作用到真实 DOM操作。

以上是 React虚拟DOM 的全部内容, 来源链接: utcz.com/a/68798.html

回到顶部