这一次,我将向您展示如何使用React虚拟DOM,以及使用React虚拟DOM时需要注意的事项。以下是实际案例。让我们来看看。
在Web开发中,需要将数据的变化实时反映在UI上,然后需要对DOM进行操作。然而,复杂或频繁的DOM操作通常是性能瓶颈的原因。所以React引入了机制DOM(虚拟DOM。
1.什么是虚拟DOM?
在React中,渲染的结果并不是一个真实的DOM节点,而是一个轻量级的JavaScript对象,我们称之为虚拟DOM。
虚拟DOM是React的一大亮点,具有批处理和高效的Diff算法。这使得我们可以随时“刷新”整个页面,而不用担心性能问题,虚拟DOM确保了只有界面上真正改变的部分才是DOM操作的。实际开发中基本不需要关心虚拟DOM是如何工作的,但是了解其运行机制不仅有助于更好地理解React组件的生命周期,还有助于进一步优化React程序。
二、虚拟DOM VS直接操作原生DOM?
如果没有虚拟DOM,就直接重置innerHTML。这样,当一个大列表中的所有数据都发生变化时,才是合理的。但是,当只有一行数据发生变化时,还需要重置整个innerHTML,这显然造成了很大的浪费。
比较innerHTML和虚拟DOM的重绘过程如下:
InnerHTML:呈现HTML字符串+重新创建所有DOM元素
虚拟DOM:渲染虚拟DOM+diff+必要的DOM更新
和DOM操作相比,js计算很便宜。虚拟DOM render+diff明显比渲染html字符串慢。不过还是纯js级别的计算,还是比下面的DOM操作便宜很多。当然也有人验证过React的性能不如直接操作真实的DOM。代码如下:
function Raw(){ var data = _ build data(),html = & quot";...for(var I = 0;我& lt数据长度;i++){ var render = template;render = render . replace(& quot;{ { className } } & quot,& quot");render = render . replace(& quot;{ { label } } & quot,数据[i]。标签);html+= render;} ...container.innerHTML = html..}在这个测试案例中,虽然构建了一个包含1000个标签的字符串并将其添加到DOM树中,但是只执行了一次DOM操作。但在实际开发过程中,这1000个元素更新可能分布在20个逻辑块中,每个逻辑块包含50个元素。当页面需要更新时,会导致DOM树更新,上面的代码大概会变成下面的格式:
function Raw(){ var data = _ build data(),html = & quot";...for(var I = 0;我& lt数据长度;i++){ var render = template;render = render . replace(& quot;{ { className } } & quot,& quot");render = render . replace(& quot;{ { label } } & quot,数据[i]。标签);html+= render;如果(!(I % 50)){ container . innerhtml = html;}} ...}这样看来,React的性能远胜于原生DOM操作。
而且DOM根本不属于Javascript(也不存在于Javascript引擎中)。实际上Javascript是一个非常独立的引擎,DOM实际上是一组从浏览器派生出来的API,允许Javascript操作HTML文档。在即时编译时代,调用DOM的开销非常大。而且虚拟DOM的执行完全在Javascript引擎中,所以没有这样的开销。
React.js比直接操作原生DOM有很大的性能优势,这很大程度上归功于虚拟DOM的批处理和diff。批处理收集所有DOM操作,并一次性提交给真正的DOM。diff算法的时间复杂度也从标准Diff算法的O(n ^ 3)降低到O(n)。留在这里,留待下一篇博客单独讨论。
三、虚拟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):
检查:范围摘要+必要的DOM更新
收集依赖:再次收集依赖+必要的DOM更新。
如您所见,Angular最低效的方面在于与任何微小变化中的观察器数量相关的性能成本。但是!当所有的数据都改变了,Angular实际上并没有受到影响。当数据初始化和更改时,需要重新收集依赖项集合。这种成本在少量数据更新时几乎可以忽略不计,但在数据庞大时也会造成一定的消耗。
当MVVM呈现一个列表时,因为每一行都有自己的数据范围,所以通常每一行都有一个相应的ViewModel实例,或者一个由prototype“scope & quot;对象,但要付出一定的代价。因此,MVVM列表渲染的初始化几乎总是比React慢,因为创建ViewModel/scope实例比创建虚拟DOM要昂贵得多。这里所有MVVM实现的一个常见问题是,当列表呈现的数据源发生变化时,尤其是当数据是全新的对象时,如何有效地重用创建的ViewModel实例和DOM元素。如果在重用上没有优化,既然数据是“全新的”,MVVM实际上需要销毁所有之前的实例,重新创建所有实例,最后再渲染一遍!这也是为什么题目中角度/击倒链接的实现比较慢的原因。相比之下,由于React的变化检查是在DOM结构层面,所以即使是全新的数据,只要最终的渲染结果没有发生变化,那么就不需要做无用功。
Angular和Vue都提供了列表重绘的优化机制,即“提示”框架如何有效重用实例和DOM元素。例如,数据库中的同一个对象在两个前端API调用中会变成不同的对象,但它们仍然具有相同的uid。这时可以提示track by uid让Angular知道这两个对象其实是相同的数据。然后这个数据对应的实例和DOM元素就可以重用了,只需要更新改变的部分。或者,可以直接按$index跟踪到“原地重用”:根据数组中的位置直接重用。在标题给出的例子中,如果angular实现添加了track by $index,那么后续的重绘不会比React慢太多。即使在dbmonster测试中,Angular和Vue在使用track by $index: dbmon后也比React快(注意Angular的默认版本没有优化,优化的在下面)
性能对比时,需要区分初始渲染、数据更新量小、数据更新量大等不同场合。虚拟DOM、脏检查MVVM和数据收集MVVM在不同的情况下具有不同的性能和不同的优化需求。为了提高小数据更新的性能,虚拟DOM也需要有针对性的优化,比如shouldComponentUpdate或者不可变数据。
初始呈现:虚拟DOM >: Check dirty >: =依赖集合
小数据更新:相关集合>:& gt虚拟DOM+优化>:脏检查(无法优化) >:虚拟DOM未优化
海量数据更新:脏查+优化>:=依赖集合+优化>:虚拟DOM(不能/不需要优化)>:& gt没有优化的MVVM
(本段借鉴了知乎的相关回答)
四、React虚拟DOM的误区?
React从来没有说过“React比原生操作DOM快”。React给出的保证是,在没有手动优化的情况下,它仍然可以为我们提供体面的性能。
React屏蔽了底层的DOM操作,可以用更声明性的方式描述我们的目的,从而使代码更容易维护。下面是知乎的回答:没有一个框架能比人工操作更快地优化DOM操作,因为框架的DOM操作层需要处理上层API可能产生的任何操作,其实现必须是通用的。对于任何基准测试,我都可以比任何框架更快地编写手动优化,但这有什么意义呢?在构建实际应用时,是否对每个地方都进行手动优化?出于可维护性的考虑,这显然是不可能的。
相信你看完这个案例已经掌握了方法。更多精彩请关注即时码站其他相关文章!
推荐阅读:
在微信小程序中上传带有后端代码的图片。
微信小程序上传图片的实际案例分析
评论前必须登录!
注册