DOM性能小记

王朝学院·作者佚名  2016-05-22
窄屏简体版  字體: 小  |  中  |  大  |  超大  

DOM性能小记在使用DOM操作时,同样的效果用不同的方式来实现,性能方面也会有很大的差异。尤其在移动式设备上,资源本来就很有限,一旦DOM写不好的话操作就会非常卡顿。这个周末,就写个DOM性能小记吧。错漏之处,望多指教。

1、浅说reflow

首先讲讲最近我才了解到的一个比较深入且模糊的东西——reflow。

从字面上理解的话,reflow有回流、重排的意思。它是指在DOM内容更新或增删时发生的一个响应过程,可以理解为页面内容改变了,然后发生重新排版这样的一个行为。我们可以将执行DOM操作细分为以下三个过程:读取HTML内容——>页面reflow——>展示更新内容。

由于reflow是一个重整布局和样式的过程,相当消耗性能。所以我们优化DOM性能实际上就是避免执行更多DOM操作,即减少reflow过程。

2、减少reflow常用方法

【1】采用临时变量存储,一次性操作DOM

以我笔记本为例,分别测试了以下两段代码:

1 //代码1 2 window.onload = function(){ 3 var oUl = document.getElementById('ul1'); 4 for (var i = 0; i < 10000; i++) { 5 oUl.innerHTML +=i; 6 } 7 } 8 //代码2 9 window.onload = function(){10 var oUl = document.getElementById('ul1');11 var str = '';12 for (var i = 0; i < 10000; i++) {13 str +=i;14 }15 oUl.innerHTML = str;16 }

在Chrome浏览器下用时取样如下,前五次为两段代码分别运行,后五次为放一起先后执行时间:

t/ms

1

2

3

4

5

6

7

8

9

10

代码1

9067.696

8900.826

9114.520

8987.592

9220.753

9344.496

8783.459

8797.731

9337.738

8835.503

代码2

4.100

1.883

2.177

2.262

2.416

5.464

2.053

1.891

2.053

1.975

从上面数据明显看得出,同样执行10000次拼接,代码二远快于代码一。

不难得出结论:减少DOM访问,改用外部变量临时存储内容,最后再一步执行DOM操作的效率远高于分次执行DOM操作。

代码二的速度约是代码一的4000倍,可粗略计算出,innerHTML每查找并赋值一次,用时就接近1ms。

【2】采用class修改多个样式,一次性更改属性

分别测试以下代码:

1 //代码三 2 window.onload = function(){ 3 var oUl = document.getElementById('ul1'); 4 for (var i = 0; i < 20000; i++) { 5 if(i%2==0){ 6 oUl.style.fontSize = "18px"; 7 oUl.style.lineHeight = "30px"; 8 oUl.style.padding = "20px"; 9 oUl.style.width = "100px";10 oUl.style.height = "100px";11 }else{12 oUl.style.fontSize = "16px";13 oUl.style.lineHeight = "40px";14 oUl.style.padding = "30px";15 oUl.style.width = "200px";16 oUl.style.height = "200px";17 }18 }19 }20 21 //代码四22 window.onload = function(){23 var oUl = document.getElementById('ul1');24 for (var i = 0; i < 20000; i++) {25 if(i%2==0){26 oUl.className = "class1";27 }else{28 oUl.className = "class2";29 }30 }31 }

同样在chrome浏览器下测试执行用时如下,前五次为两段代码分开运行,后五次为两段代码放一起运行(排除电脑不同时间点运行的差异):

t/ms

1

2

3

4

5

6

7

8

9

10

代码3

346.540

471.160

322.437

327.363

335.818

283.553

346.819

318.261

313.144

333.789

代码4

38.666

20.042

19.550

26.465

27.294

38.429

33.793

37.031

34.928

36.221

从上面的数据可以看出,通过设置class改变多个样式的方法比分次改变样式明显快得多。

在不考虑js代码赋值运算增多带来了误差的情况下(实际上很小),代码4的方法平均耗时也比代码3的方法快10多倍。因为在代码3中,我每改变一个样式都会产生一次reflow,即reflow了5次。而在代码四中使用className却只reflow了一次,但一次reflow改变了多个样式。

这里需要注意的是,不是所有样式的改变都会产生reflow的。只有会影响布局的样式才会,例如width,height,display,line-height,margin,padding,font,background等等。诸如color,opacity,visibility等则不会,因为它们只会产生repaint。你可以想象得到,改变颜色、透明度等值对页面的结构是不会产生任何影响的。这里我就不展开讲了。

【3】采用变量存储节点属性值,减少获取次数

测试代码:

1 //代码5 2 for (var i = 0; i < 10000; i++) { 3 if(i%2==0){ 4 document.getElementById('ul1').style.left = document.getElementById('ul1').offsetHeight + 10 + "px"; 5 }else{ 6 document.getElementById('ul1').style.left = document.getElementById('ul1').offsetHeight - 10 + "px"; 7 } 8 } 9 10 //代码611 var u1 = document.getElementById('ul1');12 var uW = u1.offsetHeight;13 for (var i = 0; i < 10000; i++) {14 if(i%2==0){15 u1.style.left = uW + 10 + "px";16 }else{17 u1.style.left = uW - 10 + "px";18 } 19 }

分别执行以上代码10000次的10个时间样本对比:

t/ms

1

2

3

4

5

6

7

8

9

10

代码5

1256.209

1175.189

1384.690

1241.207

1223.094

1371.524

1186.556

1176.889

1269.450

1215.922

代码6

20.633

14.353

17.901

19.665

31.405

20.503

24.451

18.909

23.883

14.472

同样可以看出,代码6是明显比代码5运行速度快的。

当我们知道某一个节点的某些属性值是确定时,这时候我们可以将这个值保存在变量中,而不需要每次重新获取。否则当运行次数达到一定量后,会严重影响程序执行的速度。因为当第一次使用变量获取某个节点的值时,页面就会只reflow一次,并把这个值保存在内存中。下次程序需要时从内存中取就可以了。而代码5中则每次都沿着document去读取这些值,这些值虽然没有改变,但这些值是浏览器需要计算才能获得的,所以每次都会reflow一次。

会导致reflow的属性有offsetLeft/Top、offsetHeight/Width、scrollTop/Left/Width/Height、clientTop/Left/Width/Height等。

养成使用这种方法的习惯,不但能提高程序性能,有时候还可以节省代码量,使程序更简洁。

说明:原创文章,转载时请注明出处,谢谢。

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航