1、前言html
网上都说操做真实dom怎么怎么慢,这儿有个例子:http://chrisharrington.github.io/demos/performance/,例子循环2000个随机数组,点击按钮从新生成随机数组渲染页面,也是本身用的js 操做dom 比用react 和angular 都要快。这是引用知乎上的一个问题。以为写的很好。文章内容主要是来源于尤大大的回答。前端
2、原生dom操做VS经过框架封装操做react
这是一个性能 vs. 可维护性的取舍。框架的意义在于为你掩盖底层的 DOM 操做,让你用更声明式的方式来描述你的目的,从而让你的代码更容易维护。没有任何框架能够比纯手动的优化 DOM 操做更快,由于框架的 DOM 操做层须要应对任何上层 API 可能产生的操做,它的实现必须是普适的。针对任何一个 benchmark,我均可以写出比任何框架更快的手动优化,可是那有什么意义呢?在构建一个实际应用的时候,你难道为每个地方都去作手动优化吗?出于可维护性的考虑,这显然不可能。框架给你的保证是,你在不须要手动优化的状况下,我依然能够给你提供过得去的性能。git
3、 对 React 的 Virtual DOM 的误解github
React 历来没有说过 “React 比原生操做 DOM 快”。React 的基本思惟模式是每次有变更就整个从新渲染整个应用。若是没有 Virtual DOM,简单来想就是直接重置 innerHTML。不少人都没有意识到,在一个大型列表全部数据都变了的状况下,重置 innerHTML 实际上是一个还算合理的操做... 真正的问题是在 “所有从新渲染” 的思惟模式下,即便只有一行数据变了,它也须要重置整个 innerHTML,这时候显然就有大量的浪费。数据库
咱们能够比较一下 innerHTML vs. Virtual DOM 的重绘性能消耗:编程
(1)innerHTML: render html string O(template size) + 从新建立全部 DOM 元素 O(DOM size)数组
(2)Virtual DOM: render Virtual DOM + diff O(template size) + 必要的 DOM 更新 O(DOM change)框架
Virtual DOM render + diff 显然比渲染 html 字符串要慢,可是!它依然是纯 js 层面的计算,比起后面的 DOM 操做来讲,依然便宜了太多。能够看到,innerHTML 的总计算量不论是 js 计算仍是 DOM 操做都是和整个界面的大小相关,但 Virtual DOM 的计算量里面,只有 js 计算和界面大小相关,DOM 操做是和数据的变更量相关的。前面说了,和 DOM 操做比起来,js 计算是极其便宜的。这才是为何要有 Virtual DOM:它保证了 1)无论你的数据变化多少,每次重绘的性能均可以接受;2) 你依然能够用相似 innerHTML 的思路去写你的应用。dom
4、 MVVM vs. Virtual DOM
相比起 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 O(watcher count) + 必要 DOM 更新 O(DOM change)
(2)依赖收集:从新收集依赖 O(data change) + 必要 DOM 更新 O(DOM change)能够看到,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 by $index 的话,后续重绘是不会比 React 慢多少的。甚至在 dbmonster 测试中,Angular 和 Vue 用了 track by $index 之后都比 React 快: dbmon (注意 Angular 默认版本无优化,优化过的在下面)
顺道说一句,React 渲染列表的时候也须要提供 key 这个特殊 prop,本质上和 track-by 是一回事。
5、性能比较也要看场合
在比较性能的时候,要分清楚初始渲染、小量数据更新、大量数据更新这些不一样的场合。Virtual DOM、脏检查 MVVM、数据收集 MVVM 在不一样场合各有不一样的表现和不一样的优化需求。Virtual DOM 为了提高小量数据更新时的性能,也须要针对性的优化,好比 shouldComponentUpdate 或是 immutable data。
(1)初始渲染:Virtual DOM > 脏检查 >= 依赖收集
(2)小量数据更新:依赖收集 >> Virtual DOM + 优化 > 脏检查(没法优化) > Virtual DOM 无优化
(3)大量数据更新:脏检查 + 优化 >= 依赖收集 + 优化 > Virtual DOM(没法/无需优化)>> MVVM 无优化
不要天真地觉得 Virtual DOM 就是快,diff 不是免费的,batching 么 MVVM 也能作,并且最终 patch 的时候还不是要用原生 API。在我看来 Virtual DOM 真正的价值历来都不是性能,而是它
(1) 为函数式的 UI 编程方式打开了大门;(2) 能够渲染到 DOM 之外的 backend,好比 ReactNative。
注:react自己遵循的就是 UI = fn(state) 这样的一个公式,这里的fn 就是函数,经过state去触发fn(在这个过程是有不少复杂的计算操做,好比Virtual DOM对比),最后致使UI的更新,不知道我理解的对不对。
6、总结
以上这些比较,更多的是对于框架开发研究者提供一些参考。主流的框架 + 合理的优化,足以应对绝大部分应用的性能需求。若是是对性能有极致需求的特殊状况,其实应该牺牲一些可维护性采起手动优化:好比 Atom 编辑器在文件渲染的实现上放弃了 React 而采用了本身实现的 tile-based rendering;又好比在移动端须要 DOM-pooling 的虚拟滚动,不须要考虑顺序变化,能够绕过框架的内置实现本身搞一个。
7、参考
尤大大的回复:https://www.zhihu.com/question/31809713/answer/53544875