APP下载

基于虚拟DOM的Web前端性能优化研究

2018-01-03戴志诚程劲草

计算机应用与软件 2017年12期
关键词:视图浏览器页面

戴志诚 程劲草

(华中师范大学国家数字化学习工程技术研究中心 湖北 武汉 430079)

基于虚拟DOM的Web前端性能优化研究

戴志诚 程劲草

(华中师范大学国家数字化学习工程技术研究中心 湖北 武汉 430079)

随着Web前端的不断发展,Web页面变得越来越复杂,强交互性带来页面状态的疯涨,用于更新页面的DOM操作也越来越多。然而频繁的DOM操作使得页面渲染缓慢,造成前端性能瓶颈。在分析造成此问题原因的基础上,介绍一种由虚拟DOM和Diff算法来优化DOM操作的方法。通过分析传统Tree-Diff与DOM-Diff算法,对现有Diff算法做出改进。最后搭建测试平台,对改进后算法(Virtual-DOM)、ReactJS以及原生JS进行渲染性能测试,并对测试结果进行对比分析。测试结果表明:虚拟DOM确实可以优化浏览器的渲染性能,且在特定的情况下,DOM-Diff改进算法效果比ReactJS更理想。

Web前端性能优化 虚拟DOM Diff算法 ReactJS

0 引 言

截止至2016年11月,Web网站总个数约14亿4 000万[1]。Web项目的规模也在不断扩大、复杂度不断提升,Web前端正高速发展。

在Web1.0时代,前端开发的主要职责是根据后台提供的数据将网页内容排版呈现出来,简单直接。但是这种基于服务器端来维护状态,然后整体刷新页面的方式对浏览器的渲染性能消耗很大。

在Web2.0时代,Ajax通信与局部页面更新机制的出现,解决了页面整体刷新导致的用户体验差的问题[2]。jQuery + 模板引擎的技术方案成为当时的主流。通过编写模板,将数据通过模板引擎渲染成视图,最后用新生成的视图替换旧的视图,达到更新页面的目的。但是这里存在着一个潜在的性能问题:如果数据量很大,最终渲染出来的DOM节点数也会很多,这样当视图需要更新时,每次都是先移除之前的DOM节点,然后再插入新的DOM节点,因为DOM操作是比较耗时的,所以会造成不小的性能开销。

随着页面的交互变得丰富细腻、内容变得庞大复杂,为了更好地维护状态和更新视图,前端MVC(Model-View Control)框架应运而生,比如著名的Backbone和AngularJS。这些框架的出现简化了前端的开发流程,屏蔽了浏览器间的兼容性问题,提高了开发效率。但是因为它们的视图层(View)仍然是基于模板而设计的,与基于jQuery + 模板引擎的方式别无二致,只不过这些工作是由框架自动完成,但并没有解决存在大量DOM操作的问题[3]。

为了同时兼顾开发效率与Web前端性能问题, Facebook率先提出了虚拟DOM的概念,并在其基础之上构建了React框架[4]。其基本思想是在视图需要更新时,首先生成当前页面的虚拟DOM,然后通过DOM-Diff算法,计算之前页面的虚拟DOM和现在页面的虚拟DOM之间的差异。接着将这些差异应用到旧的页面上,这样就尽可能地减少了对DOM的操作,提升了视图的渲染性能。

本文通过分析虚拟DOM的实现原理,并对现有的虚拟DOM核心算法进行改进,实现了一个基于虚拟DOM的基础类库Virtual-DOM,并搭建性能测试平台,对比分析Virtual-DOM与React以及原生JS在渲染同一页面下的性能表现。验证了虚拟DOM可以通过减少DOM操作数来缩短Web页面渲染时间,改进后的算法能进一步减少对处在同一层级元素的DOM操作,进一步缩短页面的渲染时间,提升用户体验。

1 DOM

DOM是用于操作XML和HTML文档的应用接口。在富客户端网页应用中, UI的更改大部分是通过DOM操作实现局部更新。

1.1 DOM操作的弊端

在浏览器中DOM与JS代码执行引擎是两个不同的模块,如图1所示。通过JS代码调用DOM实际上属于外部函数调用,每次调用都需要保存当前上下文,调用DOM接口,然后再恢复上下文。其性能损耗较模块间调用大很多。

图1 DOM与JS引擎的关系

其次,浏览器的渲染流程大致如图2所示。首先通过解析HTML文档来构建DOM树,解析CSS产生CSS规则树;然后根据DOM树和CSS规则树来构建渲染树;再执行布局过程,计算每个元素的位置;最后绘制这些元素到页面上[5]。

图2 浏览器渲染过程

而DOM操作会修改生成的DOM树或CSS规则树,导致渲染树的结构发生变化,从而产生重绘(Repaint)或者重排(Reflow)[5]。重绘是指元素的几何属性(如宽、高等)并没有发生变化,只是元素的外观(如背景色)发生改变。重排是指元素的位置或尺寸发生了改变,浏览器需要重新执行布局过程,导致渲染树的一部分或全部发生变化。重绘和重排是浏览器渲染的重要动作,对前端性能的影响非常大[6]。

频繁的DOM操作会导致页面多次重绘或重排,造成页面渲染缓慢,从而严重影响性能,也是前端性能瓶颈之所在。

1.2 DOM操作优化

DOM操作带来的性能消耗是无法避免的,优化的重点就在于尽可能地减少DOM操作。比如:① 将多次DOM操作合并为单次DOM操作(批量操作);② 把DOM元素隐藏后修改(对脱离页面布局流的DOM元素的操作不会导致渲染树的变化,也就不会导致重绘或重排)等[7]。

以上的优化方法简单明了,但是在实际的大型项目中可操作性不强,原因有二:① 在大型项目中,靠人工去优化代码,工作量太多,成本增加;② 即使花费大量的人力物力去优化代码,但由于开发人员的水平参差不齐,无法保证最终的结果能达到预期的优化程度。

而虚拟DOM就是为了解决此问题而诞生的。它自动地计算出新的页面和旧的页面之间的差异,然后将这些差异以DOM操作的形式应用到旧的页面上,不仅使页面的DOM操作降到最低,而且整个过程不需要开发人员参与,完全自动化。

2 虚拟DOM分析与改进

虚拟DOM的核心思想就是通过计算新旧页面之间的差异,然后将这些差异应用到旧的页面上,所以它主要由三个部分组成:

1) 用JS模拟真实的DOM。

2) 对比两棵虚拟DOM树之间的差异。

3) 将差异应用到旧的页面上。

下面将从这三个方面来分析虚拟DOM,并对现有的DOM-Diff算法进行改进。

2.1 用JS对象模拟真实DOM

虚拟DOM就是用JS对象来表示真实DOM节点的信息与结构,保留它的节点类型、基本属性还有层次关系。这比创建真实的DOM节点代价小很多,而且虚拟DOM已经包含了创建真实DOM所需要的所有信息,最后完全可以用这个JS对象构建一棵真正的DOM树。具体流程如图3所示。

图3 创建JS对象

2.2 DOM-Diff

DOM-Diff是整个虚拟DOM理论的核心。整个DOM-Diff的流程概括为:当页面状态发生变化时,重新构造一棵新的JS对象树,用新的对象树和旧的对象树进行比较,利用DOM-Diff算法,记录两棵对象树的差异。

2.2.1 传统 Tree-Diff 算法

计算两棵树形结构之间转换需要的最少操作步骤的算法有很多,相关的研究也很多[8]。传统的Diff算法通过循环递归对节点进行一次对比,复杂度为O(n3),其中n是节点的总数。如果树形结构包含1 000个节点,就会需要进行上10亿次的差异比较。对于前端渲染场景来说,这种指数型的性能消耗代价太高,明显不能满足这种频繁更新的页面渲染需求。

2.2.2 DOM-Diff 算法

大部分页面变更前和变更后DOM树的结构基本相似,且很少会有跨层级DOM元素移动,所以可以舍弃很多复杂的移位比较。这里DOM-Diff算法,参考ReactJS对传统Tree Diff算法做出两点假设:

1) 只对树中同层级的节点进行差异对比,而对于不同层级的节点仅进行创建或删除操作。当发现某个节点在新的树中已被删除,那么其所有的子节点也会被一同删掉,不会再递归进行子节点的差异比较。算法复杂度由此降为O(n)。

2) 对于同一层的一组子节点,可以通过唯一的Key值进行区分定位,在对列表进行添加和删除操作时,只需要给出新的列表元素的顺序,就可以有效地复用DOM元素[9]。

2.2.3 DOM- Diff 算法改进与实现

以上DOM-Diff算法思想,已被应用于ReactJS类库中。通过对ReactJS源码进行分析,发现ReactJS进行同层元素(有唯一的key)对比的过程如下:

1) 对新集合的节点进行循环遍历,通过唯一的key判断旧集合中是否存在相同的节点。

2) 如果存在相同节点就进行移动操作。在移动前需要将当前结点在旧集合中的位置(child._mountIndex)与访问过的节点在旧集合中最右的位置(lastIndex)比较。如果(child._mountIndex < lastIndex),则进行节点移动操作,否则不执行该操作。不存在相同节点的节点则执行插入操作。

3) 对旧集合的节点进行循环遍历,删除新集合中不存在的节点。

分析如图4所示情况,旧集合的节点为:A、B、C、D,新集合的节点为D、A、B、C,理论上新旧集合转换需要的最小操作步骤为1,即仅D节点发生移动。但是因为D节点在旧集合的位置最大,其他节点的(_mountIndex < lastIndex),所以造成 D节点没有执行任何操作,而是 A、B、C 节点全部执行移动操作的现象。

图4 队尾移动至队首

当节点数量过大或者更新操作过于频繁时,上述情况在一定程度上也会影响到ReactJS的渲染性能,针对这一现象我们做了如下改进。

在做虚拟DOM元素的同层差异对比时,先顺序Diff,判断当前结点在旧集合中的位置是否小于旧集合中访问过的最右的位置,若小于,进行移动操作(child._mountIndex < lastIndex);然后再逆序Diff,判断当前结点在旧集合中的位置是否大于旧集合中访问过的最右的位置时,若大于,进行移动操作(child._mountIndex > lastIndex);最后比较哪种运算需要的操作少,就记录少的操作到差异对象数组中。具体流程如图5所示。

图5 List-Diff算法流程

2.3 将差异补丁应用到旧页面

将差异补丁应用到旧的页面,是整个虚拟DOM的最后一个环节。通过对差异补丁进行遍历,根据每个补丁的类型(增加、修改或移动),将其应用到与之对应的旧页面上的DOM元素(对DOM元素只想相应的增加、修改或移动操作),至此旧页面变成新页面,视图完成更新。整个过程如图6所示。

图6 将补丁应用到旧页面

3 实验分析

3.1 实验环境及内容

实验工具为Chrome浏览器54.0版本。

搭建虚拟DOM性能测试平台,对Virtual-DOM、ReactJS以及原生JS进行渲染性能测试,使用他们分别来动态添加、修改、过滤一定的随机列表数据,再将他们渲染到各自的容器中,记录并分析每组实验中三者的渲染时间。比较Virtual-DOM对浏览器渲染的优化程度。

3.2 实验结果与分析

3.2.1 测试平台与测试数据展示

测试平台与测试数据如图7所示。

图7 测试平台展示

3.2.2 测试结果与分析

测试分为横向测试和纵向测试,根据输入的测试迭代次数和单次测试数据量不同,测试案例不同。横向测试用作对比Virtual-DOM、ReactJS以及原生JS三者在渲染同一组随机数据的性能优劣。纵向测试用作自身的对比,对测试数据进行曲线拟合,分析算法本身的复杂度以及算法的稳定性。

横向比较:

1) 插入10 000条数据

实验结果与分析:

如图8所示,添加数据时Virtual-DOM平均用时208.6毫秒、ReactJS平均用时865.9毫秒、原生JS平均用时66毫秒,原生JS的实验结果最佳。

图8 插入10 000条数据

原因是ReactJS以及Virtual-DOM都有先构建一个虚拟DOM,再将这个内存中的虚拟DOM映射到页面中的过程,所以耗时会增加。同时可以发现Virtual-DOM的实验结果比ReactJS好,因为ReactJS类库很大,本身不止包含虚拟DOM,还有组件化,生命周期等,所以在简单的测试案例中一定比单纯的Virtual-DOM来说耗时会增加。

2) 修改10 000条数据

实验结果与分析:

如图9所示,修改数据时Virtual-DOM平均用时219.4毫秒、ReactJS平均用时571.4毫秒、原生JS平均用时3 355.1毫秒,Virtual-DOM的实验结果最佳。

图9 修改10 000条数据

(1) 综合来看,更新数据时,Virtual-DOM以及ReactJS两者使用虚拟DOM的效果都比没有专门针对优化的原生JS效果更好,且多次实验表明处理数据越多,效果越好。验证了虚拟DOM与Diff算法可以减少浏览器渲染时长,优化Web应用的性能。

(2) 由于测试平台是在Chrome浏览器上运行,由于V8引擎的即时编译(JIT)技术比较特殊,它对代码进行了两次编译,第一次粗编译,第二次会把执行次数很多的函数进行精细编译。所以前两次点击运行的时候所耗的时间 = 框架被编译的时间(JIT) + 执行时间,之后执行的时间 = 执行时间。所以第三次以后才是真实的执行时间,应该从第三次以后开始观察。

(3) ReactJS耗时是Virtual-DOM的三倍。验证了改进的DOM-Diff算法的可行性与有效性。

3) 过滤10 000条数据

实验结果与分析:

如图10所示,修改数据时Virtual-DOM平均用时96.9毫秒、ReactJS平均用时292.6毫秒、原生JS平均用时772.6毫秒,Virtual-DOM的实验结果最佳。原因同更新操作。

图10 过滤10 000条数据

纵向比较:

1) 插入0~10 000条数据

实验结果与分析:

如图11所示,经过曲线拟合,Virtual-DOM、ReactJS以及原生JS三者的时间复杂度都是O(n),结果呈线性、算法稳定。由斜率可以看到,随着测试数据的不断增加,Virtual-DOM的性能也是可以被接收的。当然ReactJS也是可以被优化的,但这里不做过多讨论。

图11 插入0~10 000条数据

2) 修改0~10 000条数据与过滤0~10 000条数据

实验结果与分析:

对Virtual-DOM、ReactJS以及原生JS三者进行修改数据和过滤数据纵向测试,结果如图12、图13所示。依据最小二乘法的拟合原理[10],借助MATLAB工具对纵向测试中的实验数据进行拟合,并将纵向修改测试的拟合结果如图14所示。原生JS的时间复杂度为O(n2), Virtual-DOM与ReactJS的时间复杂度为O(n)。同样验证了当页面需要频繁操作DOM时,虚拟DOM会带来更好的性能和用户体验。

图12 修改0~10 000条数据

图13 过滤0~10 000条数据

图14 纵向修改测试拟合结果

4 结 语

本文介绍了基于虚拟DOM的页面渲染优化,改进了DOM Diff算法,并实现了基于改进后的Diff算法的一个Virtual-DOM类库。通过实验对比相同情况下Virtual-DOM、ReactJS以及原生JS的渲染结果,得出结论:虚拟DOM在页面频繁变更的情况下可以很大程度地减少屏幕响应时间,大大改善用户体验,提升应用性能。通过实验证明虚拟DOM与Diff算法的完美结合,保证了每次更新操作后页面的高效渲染,使得用户无需顾忌性能问题而使用更直观简介的开发方式。

[1] Netcraft.November 2016 Web Server Survey[EB/OL].https://news.netcraft.com/archives/2016/11/22/november-2016-web-server-survey.html.

[2] 杨俊峰,黎建辉,杨风雷.深层网站Ajax页面数据采集研究综述[J].计算机应用研究,2013,30(6):1606-1610,1616.

[3] Huey Petersen.Angular Is Show[EB/OL].Hueypetersen.com/posts/2013/06/17/angular_is_slow/.

[4] Pete Hunt.Why did we build React?[EB/OL].https://facebook.github.io/react/blog/2013/06/05/why-react.html.

[5] Incapsula.How browsers work[EB/OL].http://taligarsiel.com/Projects/howbrowserswork1.htm.

[6] 赵小厦,范冰冰,夏嵬.基于WebKit的一种渲染改进方法[J].计算机应用与软件,2014,31(1):246-248.

[7] 王成,李少元,郑黎晓,等.Web前端性能优化方案与实践[J].计算机应用与软件,2014,31(12):89-95,147.

[8] Bille P.A survey on tree edit distance and related problems[J].Theoretical Computer Science,2005,337:217-239.

[9] 陈屹.深入React技术栈[M].北京:人民邮电出版社,2016:172-183.

[10] 蔡山,张浩,陈洪辉,等.基于最小二乘法的分段三次曲线拟合方法研究[J].科学技术与工程,2007,2(3):352-355.

RESEARCHOFOPTIMIZATIONFORWEBFRONT-ENDPERFORMANCEBASEDONVIRTUALDOM

Dai Zhicheng Cheng Jingcao

(NationalEngineeringResearchCenterforE-Learning,CentralChinaNormalUniversity,Wuhan430079,Hubei,China)

With the constant development of the web front-end, web page has become more complex. Strong interaction leads to a crazy rise in page status. DOM operation for updating the page will also increase. However, frequent DOM operation makes slow page rendering, resulting in front-end performance bottlenecks. Therefore, based on the analysis of the causes of this problem, a method of optimizing DOM operation by virtual DOM and Diff algorithm was introduced. Moreover, by analyzing the traditional Tree-Diff and DOM-Diff algorithm, the existing Diff algorithm was improved. Finally, a test platform was built to test the rendering performance of the improved algorithm (Virtual-DOM), ReactJS and native JS, and the test results were compared and analyzed. The experimental results show that the virtual DOM can really optimize the rendering performance of the browser. And in certain circumstances, the improved DOM-Diff algorithm is better than ReactJS.

Web front-end performance optimization Virtual DOM Diff algorithm ReactJS

2016-12-25。国家科技支撑计划子课题(2015BAH33F0202)。戴志诚,副教授,主研领域:教育信息化。程劲草,硕士生。

TP302.7

A

10.3969/j.issn.1000-386x.2017.12.004

猜你喜欢

视图浏览器页面
刷新生活的页面
答案
让Word同时拥有横向页和纵向页
微软发布新Edge浏览器预览版下载换装Chrome内核
反浏览器指纹追踪
视图
Y—20重型运输机多视图
SA2型76毫米车载高炮多视图
Django 框架中通用类视图的用法
浏览器