深刻解析React的之虚拟DOM

在Web开发中,须要将数据的变化实时反映到UI上,这时就须要对DOM进行操做,可是复杂或频繁的DOM操做一般是性能瓶颈产生的缘由,为此,React引入了虚拟DOM(Virtual DOM)的机制。css

1、什么是虚拟DOM?html

  • 在React中,render执行的结果获得的并非真正的DOM节点,结果仅仅是轻量级的JavaScript对象,咱们称之为virtual DOM。
  • 虚拟DOM是React的一大亮点,具备batching(批处理)和高效的Diff算法。这让咱们能够无需担忧性能问题而”毫无顾忌”的随时“刷新”整个页面,由虚拟 DOM来确保只对界面上真正变化的部分进行实际的DOM操做。在实际开发中基本无需关心虚拟DOM是如何运做的,可是理解其运行机制不只有助于更好的理解React组件的生命周期,并且对于进一步优化 React程序也会有很大帮助。

2、虚拟DOM VS 直接操做原生DOM? 若是没有 Virtual DOM,简单来讲就是直接重置 innerHTML。这样操做,在一个大型列表全部数据都变了的状况下,还算是合理,可是,当只有一行数据发生变化时,它也须要重置整个 innerHTML,这时候显然就形成了大量浪费。 比较innerHTML 和Virtual DOM 的重绘过程以下:前端

  • innerHTML: render html string + 从新建立全部 DOM 元素
  • Virtual DOM: render Virtual DOM + diff + 必要的 DOM 更新

和 DOM 操做比起来,js 计算是很是便宜的。Virtual DOM render + diff 显然比渲染 html 字符串要慢,可是,它依然是纯 js 层面的计算,比起后面的 DOM 操做来讲,依然便宜了太多。固然,曾有人作过验证说React的性能不如直接操做真实DOM,代码以下:vue

function Raw() {
 var data = _buildData(),
    html = "";
  ...
  for(var i=0; i<data.length; i++) {
    var render = template;
    render = render.replace("{{className}}", "");
    render = render.replace("{{label}}", data[i].label);
    html += render;//欢迎加入全栈开发交流圈一块儿学习交流:864305860
  }//面向1-3年前端人员
  ...//帮助突破技术瓶颈,提高思惟能力
  container.innerHTML = html;
  ...
}
复制代码

虽然构造了一个包含1000个Tag的String,并把它添加到DOM树中,可是只作了一次DOM操做。然而,在实际开发过程当中,这1000个元素更新可能分布在20个逻辑块中,每一个逻辑块中包含50个元素,当页面须要更新时,都会引发DOM树的更新,上述代码就近似变成了以下格式:node

function Raw() {
  var data = _buildData(), 
    html = ""; 
  ... 
  for(var i=0; i<data.length; i++) { 
    var render = template; 
    render = render.replace("{{className}}", ""); 
    render = render.replace("{{label}}", data[i].label); 
    html += render; 
    if(!(i % 50)) {
      container.innerHTML = html;
    }//欢迎加入全栈开发交流圈一块儿学习交流:864305860
  } //面向1-3年前端人员
  ... //帮助突破技术瓶颈,提高思惟能力
}
复制代码

React的性能就远胜于原生DOM操做了。webpack

并且,DOM 彻底不属于Javascript (也不在Javascript 引擎中存在).。Javascript 实际上是一个很是独立的引擎,DOM实际上是浏览器引出的一组让Javascript操做HTML文档的API而已。在即时编译的时代,调用DOM的开销是很大的。而Virtual DOM的执行彻底都在Javascript 引擎中,彻底不会有这个开销。web

React.js 相对于直接操做原生DOM有很大的性能优点, 很大程度上都要归功于virtual DOM的batching 和diff。batching把全部的DOM操做搜集起来,一次性提交给真实的DOM。diff算法时间复杂度也从**[标准的的Diff算法]**的O(n^3)降到了O(n)。这里留到下一次博客单独讲。面试

3、虚拟DOM VS MVVM?算法

相比起 React,其余 MVVM 系框架好比 Angular, Knockout 以及 Vue、Avalon 采用的都是数据绑定:经过 Directive/Binding 对象,观察数据变化并保留对实际 DOM 元素的引用,当有数据变化时进行对应的操做。MVVM 的变化检查是数据层面的,而 React 的检查是 DOM 结构层面的。MVVM 的性能也根据变更检测的实现原理有所不一样:Angular 的脏检查使得任何变更都有固定的 O(watcher count) 的代价;Knockout/Vue/Avalon 都采用了依赖收集,在 js 和 DOM 层面都是 O(change):数据库

  1. 脏检查:scope digest + 必要 DOM 更新
  2. 依赖收集:从新收集依赖 + 必要 DOM 更新

能够看到,Angular 最不效率的地方在于任何小变更都有的和 watcher 数量相关的性能代价。可是!当全部数据都变了的时候,Angular 其实并不吃亏。依赖收集在初始化和数据变化的时候都须要从新收集依赖,这个代价在小量更新的时候几乎能够忽略,但在数据量庞大的时候也会产生必定的消耗。

MVVM 渲染列表的时候,因为每一行都有本身的数据做用域,因此一般都是每一行有一个对应的 ViewModel 实例,或者是一个稍微轻量一些的利用原型继承的 "scope" 对象,但也有必定的代价。因此,MVVM 列表渲染的初始化几乎必定比 React 慢,由于建立 ViewModel / scope 实例比起 Virtual DOM 来讲要昂贵不少。这里全部 MVVM 实现的一个共同问题就是在列表渲染的数据源变更时,尤为是当数据是全新的对象时,如何有效地复用已经建立的 ViewModel 实例和 DOM 元素。假如没有任何复用方面的优化,因为数据是 “全新” 的,MVVM 实际上须要销毁以前的全部实例,从新建立全部实例,最后再进行一次渲染!这就是为何题目里连接的 angular/knockout 实现都相对比较慢。相比之下,React 的变更检查因为是 DOM 结构层面的,即便是全新的数据,只要最后渲染结果没变,那么就不须要作无用功。

Angular 和 Vue 都提供了列表重绘的优化机制,也就是 “提示” 框架如何有效地复用实例和 DOM 元素。好比数据库里的同一个对象,在两次前端 API 调用里面会成为不一样的对象,可是它们依然有同样的 uid。这时候你就能够提示 track by uid 来让 Angular 知道,这两个对象实际上是同一份数据。那么原来这份数据对应的实例和 DOM 元素均可以复用,只须要更新变更了的部分。或者,你也能够直接 track by index 来进行 “原地复用”:直接根据在数组里的位置进行复用。在题目给出的例子里,若是 angular 实现加上 track byindex 的话,后续重绘是不会比 React 慢多少的。甚至在 dbmonster 测试中,Angular 和 Vue 用了 track by $index 之后都比 React 快:[dbmon] (注意 Angular 默认版本无优化,优化过的在下面)

在比较性能的时候,要分清楚初始渲染、小量数据更新、大量数据更新这些不一样的场合。Virtual DOM、脏检查 MVVM、数据收集 MVVM 在不一样场合各有不一样的表现和不一样的优化需求。Virtual DOM 为了提高小量数据更新时的性能,也须要针对性的优化,好比 shouldComponentUpdate 或是 immutable data。

  1. 初始渲染:Virtual DOM > 脏检查 >= 依赖收集
  2. 小量数据更新:依赖收集 >> Virtual DOM + 优化 > 脏检查(没法优化) > Virtual DOM 无优化
  3. 大量数据更新:脏检查 + 优化 >= 依赖收集 + 优化 > Virtual DOM(没法/无需优化)>> MVVM 无优化
  4. (该段落借鉴了知乎的相关回答)

4、对React虚拟DOM的误解?

React 历来没有说过 “React 比原生操做 DOM 快”。React给咱们的保证是,在不须要手动优化的状况下,它依然能够给咱们提供过得去的性能。

React掩盖了底层的 DOM 操做,能够用更声明式的方式来描述咱们目的,从而让代码更容易维护。下面仍是借鉴了知乎上的回答:没有任何框架能够比纯手动的优化 DOM 操做更快,由于框架的 DOM 操做层须要应对任何上层 API 可能产生的操做,它的实现必须是普适的。针对任何一个 benchmark,我均可以写出比任何框架更快的手动优化,可是那有什么意义呢?在构建一个实际应用的时候,你难道为每个地方都去作手动优化吗?出于可维护性的考虑,这显然不可能。 结语

感谢您的观看,若有不足之处,欢迎批评指正。 本次给你们推荐一个免费的学习群,里面归纳移动应用网站开发,css,html,webpack,vue node angular以及面试资源等。 对web开发技术感兴趣的同窗,欢迎加入Q群:864305860,无论你是小白仍是大牛我都欢迎,还有大牛整理的一套高效率学习路线和教程与您免费分享,同时天天更新视频资料。 最后,祝你们早日学有所成,拿到满意offer,快速升职加薪,走上人生巅峰。

相关文章
相关标签/搜索