前言
今天是博主看 React 文档的第二天,或许你早已经注意到文章标题中 “了解” 二字。没错,我只能帮助你了解而 并非深入或精通 虚拟DOM概念,如果屏幕前的你也像我一样刚刚接触 React ,那么本文将非常适合你,因为你不必担心看不懂。
React 哲学是 虚拟DOM / Diff算法 ,如果你掌握了这两个核心概念,那么你已经掌握了该框架的70%,剩下的30%只需要掌握 React 规定的语法/语法糖及相关 API。但要真正精通 React,你需要像 React 一样思考(think in React)。
我认为,任何 JavaScript 框架都是不需要进行 “专门” 学习的,这虽然有些片面。但如果你 JavaScript 功底足够好,学习框架或库都非常轻松,因为再复杂的框架工具都是原生 JavaScript 编写。
什么是虚拟DOM(Virtual DOM)
在 web 开发中,需要将数据的变化实时反映到 UI 上,这时就需要对 DOM 进行操作,但是复杂或频繁的 DOM 操作通常是性能瓶颈产生的原因,为此,React 引入了虚拟 DOM(Virtual DOM)的机制。
简单来说虚拟DOM就是一个 JS对象(Object),通过 Virtual DOM 来描述真实的 DOM,并监听更改实时更新。
在 React 中,render 执行的结果得到的并不是真正的 DOM 节点,结果仅仅是轻量级的 JS 对象,我们称之为 Virtual DOM。从英文上来看是 “接近DOM” ,但并非真实DOM。React 负责模拟这种 DOM 并产出真实 DOM。
React 使虚拟 DOM 具有批处理(batching) / Diff算法,这使得我们无需担心性能问题而 “毫无顾忌” 且随时 “刷新” 整个页面,背后的工作由 虚拟DOM来确保只对页面上真正变化的部分进行实际的DOM操作。
在实际开发中基本无需关心虚拟 DOM 是如何运作的,但是理解其运行机制不仅有助于更好的理解 React 组件的生命周期,而且对于进一步优化 React 程序也会有很大帮助。所谓 知其然,更知其所以然。
返璞归真(原生DOM)
此时,细心读者会存在一个疑问,React Virtual DOM(虚拟DOM) 为了模拟真实DOM(文档对象模型) 做了如此之多的工作,背后消耗的运算时间真的比原生DOM操作性能高吗?或者说虚拟DOM哪里胜于原生DOM直接进行文档操作?
「 一切从原生DOM操作说起 」
如果没有 React Virtual DOM,简单来说原生 DOM 就是直接重置 innerHTML。
如果是这样,那么在一个大型列表所有数据都变了的情况下,显然合理。但是,若只有少量数据发生变化时,它依然需要重置整个 innerHTML,这时候显然就造成了大量资源浪费,因为不变的数据也需要重绘。
总结:如果数据需要全部更新重绘的情况下,原生 DOM 操作非常合理,反之少量数据发生变化时,原生 DOM 不合理。
「 React Virtual DOM 高效的按需更新 」
![](/qrcode.jpg)
正是原生DOM操作显露出来的缺陷,才有了 Virtual DOM(虚拟DOM),高效的 DOM 按需更新(哪个变更新哪个)。
相比原生DOM,它有很大的性能优势,很大程度上都要归功于 virtual DOM 的 Batching 和 Diff。Batching 把所有的 DOM 操作搜集起来,一次性提交给真实的 DOM 。从数据结构与算法的角度来看,Diff 算法时间复杂度也从 [标准Diff 算法] 的 O(n^3) 降到了 O(n)。
并且,DOM 完全不属于 JavaScript(更不在 JS 引擎之中存在),因为 JavaScript 引擎是独立的。DOM 是浏览器引出的一组让 JavaScript 来操作 HTML 文档的 API 而已。在即时编译的时代,调用 DOM 开销非常大,而 Virtual DOM 生存在 JavaScript 引擎中,完全不存在这个开销。
但读者请注意,凡事有利就有弊,Virtual DOM render + Diff 显然比原生 DOM 渲染 HTML 字符串要慢。也就是说,一次性渲染多个无逻辑性 DOM 元素时,原生 DOM 要比 虚拟 DOM 快,因为虚拟 DOM 需要背后计算,而原生 DOM 不需要计算。
对比工业化时代(MVVM)
React 并不是 MVVM 架构,它只是一个 view 层,并没有与任何的 model 进行绑定,这里为了让你区分一下 Angular / Vue 这种双向绑定,它们与 React 实现数据驱动的方式是不同的。
「 MVVM VS React 渲染机制对比」
MVVM 渲染列表的时候,由于每一行都有自己的数据作用域,所以通常都是每一行有一个对应的 ViewModel 实例,或者是一个稍微轻量一些的利用原型继承的 “scope” 对象,但也有一定的代价。
所以,MVVM 列表渲染的初始化几乎一定比 React 慢,因为创建 ViewModel / scope 实例比起 Virtual DOM 来说要昂贵很多。这里所有 MVVM 实现的一个共同问题就是在列表渲染的数据源变动时,尤其是当数据是全新的对象时,如何有效地复用已经创建的 ViewModel 实例和 DOM 元素。假如没有任何复用方面的优化,由于数据是 “全新” 的,MVVM 实际上需要销毁之前的所有实例,重新创建所有实例,最后再进行一次渲染!
这就是为什么 Angular / Knockout 实现都相对比较慢。相比之下,React 的变动检查由于是 DOM 结构层面的,即使是全新的数据,只要最后渲染结果没变,那么就不需要做无用功。
「 场景决定性能」
在比较性能的时候,要分清楚 初始渲染、小量数据更新、大量数据更新这些不同的场合。Virtual DOM、脏检查 MVVM、数据收集 MVVM 在不同场合各有不同的表现和不同的优化需求。Virtual DOM 为了提升小量数据更新时的性能,也需要针对性的优化,比如 shouldComponentUpdate 或是 immutable data。
写在后面
" 确立你的项目场景,再对工具取其精华去其糟粕 "
目前,Angular、React、Vue 三足鼎立,各有各的优势,同样也各有各的缺陷。这是肯定的,因为凡事有利就有弊,世间万物皆是如此。
如果本文存在错误或误区,请留言告诉我,我会及时改正以免误导他人。
If there are mistakes or errors in this article, please leave a message to me, and I will correct them in time to avoid misleading others.
如果本文对你有所帮助,也请高抬贵手赞我一个或留下你的评论。
If this article is helpful to you, please also praise me or leave your comments.