前端页面卡顿?或是DOM操作惹的妨害,需优化代码

文档对象模型(DOM)是一个独立
于特定语言的应用程序接口。在浏览器被,DOM接口是为JavaScript语言实现之,通过JavaScript来操作浏览器页面被的元素,这让
DOM成为了JavaScript中一言九鼎的片段。在富客户端网页应用中,界面及UI的反都是经过DOM操作实现之,并无是透过传统的基础代谢页面实现
的。尽管DOM提供了增长接口供外部调用,但DOM操作的代价十分高,页面前端代码的特性瓶颈也大都集中在DOM操作及,所以前端性能优化的一个首要的体贴
点就是DOM操作的优化。DOM操作优化的终究原则是尽量减少DOM操作。

图片 1

每当议论DOM操作的极品性能实践之前,先来探DOM操作为什么会潜移默化性。在浏览器中,DOM的实现和ECMAScript的实现是分离之。比如
在IE中,ECMAScrit的贯彻在jscript.dll中,而DOM的落实以mshtml.dll中;在Chrome中利用WebKit中的
WebCore处理DOM和渲染,但ECMAScript是于V8引擎中贯彻之,其他浏览器的景况好像。所以通过JavaScript代码调用DOM接
口,相当给片独独立模块的竞相。相较在同一模块中的调用,这种跨模块的调用其特性损耗是挺高之。但DOM操作对性能影响无与伦比老实际要以它们导致了浏览器
的重绘(repaint)和重排(reflow)。

为了给大家能更厚地掌握重绘和重排对性的影响,这里需要简单描述一下浏览器的渲染原理(如果想详细了解浏览器的工作规律,请参见文章《浏览器的行事规律:新式网络浏览器幕后揭开》
)。从下载文档到渲染页面的过程中,浏览器会通过解析HTML文档来构建DOM树,解析CSS产生CSS规则树。JavaScript代码在分析过程被,
可能会见修改生成的DOM树和CSS规则树。之后据悉DOM树和CSS规则树构建渲染树,在这个进程中CSS会根据选择器匹配HTML元素。渲染树包括了每
个因素的大小、边距等体制属性,渲染树被未分包隐藏元素及head元素等不可见元素。最后浏览器根据元素的坐标和分寸来计量每个元素的职,并绘制这些元
素到页面上。重绘指的是页面的少数部分如再次绘制,比如颜色要背景色的修改,元素的岗位以及尺寸并不曾因此改变;重排则是因素的职位或尺寸发生了移,浏览器需
要又计算渲染树,导致渲染树的同等局部或者全发出变更。渲染树还建后,浏览器会重新绘制页面上受影响之素。重排的代价比重绘的代价高多,重绘会影
响部分的元素,而重排则发出或影响总体之因素。如下的这些DOM操作会招重绘或重排:

  • 多、删除和改动可见DOM元素

  • 页面初始化的渲染

  • 移动DOM元素

  • 改CSS样式,改变DOM元素的尺码

  • DOM元素内容改动,使得尺寸为撑大

  • 浏览器窗口尺寸改变

  • 浏览器窗口滚动

足见见,这些操作都是DOM操作着于常见的。现代浏览器会针对重排或重绘做性能优化,比如,把DOM操作积累一批判后统一做同样糟重排或重绘。但在聊情况下,浏览器会应声重排或重绘。比如要如下的DOM元素布局信息:offsetTop/Left/Width/HeightscrollTop/Left/Width/HeightclientTop/Left/Width/HeightgetComputedStyle()或 currentStyle。因为这些价值都是动态计算的,所以浏览器需要及早形成页面的绘图,然后计算返回值,从而打乱了重排或重绘的优化。

DOM操作带来的页面重绘或重排是不可避免的,但可按一些顶尖级实践来下滑由于重排或重绘带来的影响。如下是一对现实的推行措施:

  1. 集合往往底DOM操作为单次的DOM操作

最为常见频繁进行DOM操作的是反复修改DOM元素的体裁,代码类似如下:

element.style.borderColor = '#f00';
element.style.borderStyle = 'solid';
element.style.borderWidth = '1px';

这种编码方式会坐反复更改DOM元素的样式,触发页面多次底重排或重绘,上面介绍过,现代浏览器针对这种状态时有发生总体性的优化,它见面合并DOM操作,但并无是颇具的浏览器还设有这么的优化。推荐的主意是将DOM操作尽量合并,如达到的代码可以优化为:

// 优化方案1
element.style.cssText += 'border: 1px solid #f00;';
// 优化方案2
element.className += 'empty';

演示的代码来半点种植优化的方案,都做到了把多次的样式设置统一为同一涂鸦设置。方案2于方案1略粗发一些性质达到的消耗,因为它们用查询CSS类。但方案2的维护性最好,这在直达亦然章就讨论过。很多下,如果性能问题并无突出,选择编码方案时欲先考虑的是代码的维护性。

看似之操作还有通过innerHTML接口修改DOM元素的始末。不要一直通过这接口来并接HTML代码,而是因为字符串方式拼接好代码后,一次性赋值给DOM元素的innerHTML接口。

  1. 管DOM元素离线或藏后改

拿DOM元素于页面流中脱离或潜伏,这样处理后,只会于DOM元素脱离和丰富时,或者是隐蔽和显示时才会招致页面的重绘或重排,对退出了页面布局流的DOM元素操作就非会见造成页面的性能问题。这种方法可那些急需大批量窜DOM元素的情况。具体的方要出三栽:

(1)使用文档片段

文档片段是一个轻量级的document对象,并无会见暨一定的页面关联。通过当文档片段上开展DOM操作,可以减低DOM操作对页面性能的影响,这
种办法是创造一个文档片段,并当这个部分上进展必要之DOM操作,操作就后以它附加在页面中。对页面性能的影响就存在被最后将文档片段附加到页面的立同一步
操作上。代码类似如下:

var fragment = document.createDocumentFragment();
// 一些基于fragment的大量DOM操作
...
document.getElementById('myElement').appendChild(fragment);

(2)通过安装DOM元素的display样式为none来隐藏元素

这种办法是经隐蔽页面的DOM元素,达到以页面中移除元素的成效,经过大量之DOM操作后恢复元素原来的display样式。对于当下类似会滋生页面重绘或重排的操作,就惟有隐藏和出示DOM元素就简单单步骤了。代码类似如下:

var myElement = document.getElementById('myElement');
myElement.style.display = 'none';
// 一些基于myElement的大量DOM操作
...
myElement.style.display = 'block';

(3)克隆DOM元素到外存中

这种方式是管页面及之DOM元素克隆一客到内存中,然后再次在内存中操作克隆的要素,操作完成后动此克隆元素替换页面中本的DOM元素。这样一来,影响属性的操作就只是最后替换元素的就等同步操作了,在内存中操作克隆元素不会见挑起页面及之性损耗。代码类似如下:

var old = document.getElementById('myElement');
var clone = old.cloneNode(true);
// 一些基于clone的大量DOM操作
...
old.parentNode.replaceChild(clone, old);

当现代的浏览器被,因为来了DOM操作的优化,所以采取如达到的方法后也许连无克明了感受及性的改进。但是在还占据市场之有的原有浏览器被,应用以上就三种编码方式则可大幅提高页面渲染性能。

  1. 安装有动画效果的DOM元素的position属性为fixed或absolute

拿页面被兼有动画效果的要素设置也绝对定位,使得元素脱离页面布局流,从而避免了页面频繁之重排,只涉嫌动画元素自身的重排了。这种做法得以增长动
画作用的来得性质。如果管动画元素设置也绝对定位并无抱规划的渴求,则可以于动画开始时用那安装为绝对定位,等动画片结束晚恢复原来之原则性装置。在多底
网站中,页面的顶部会生大幅的广告展示,一般会动画展开和折叠显示。如果未做性能的优化,这个力量的习性损耗是大明朗的。使用此涉及的优化方案,则可
提高性。

  1. 小心谨慎得DOM元素的布局信息

前方议论了,获取DOM的布局信息会生总体性的淘,所以要在双重调用,最佳的做法是拼命三郎把这些值缓存在一些变量中。考虑如下的一个示范:

for (var i=0; i < len; i++) {
    myElements[i].style.top = targetElement.offsetTop + i*5 + 'px';
}

假如齐之代码中,会当一个巡回中屡屡取得一个素的offsetTop价,事实上,在此代码中该因素的offsetTop价值并无会见变动,所以会见有无必要的性能损耗。优化的方案是以循环外部得到元素的offsetTop值,相较前面的方案,此方案只是调用了同样全勤元素的offsetTop价值。更改后的代码如下:

var targetTop = targetElement.offsetTop;
for (var i=0; i < len; i++) {
    myElements[i].style.top = targetTop+ i*5 + 'px';
}

另外,因为获DOM元素的布局信息会强制浏览器刷新渲染树,并且可能会见导致页面的重绘或重排,所以在有大批量DOM操作时,应避免获取DOM元素
的布局信息,使得浏览器针对大批量DOM操作的优化不吃弄坏。如果要这些布局信息,最好是当DOM操作前便取。考虑如下一个示范:

`var newWidth = div1.offsetWidth + 10;
div1.style.width = newWidth + ‘px’;

var newHeight = myElement.offsetHeight + 10; // 强制页面重排
myElement.style.height = newHeight + ‘px’; // 又见面再也排一不好`

基于上面的介绍,代码在遇见取得DOM元素的信经常会见沾页面还计算渲染树,所以只要齐之代码会造成页面重排两次等,如果拿收获DOM元素的布局信息提前,因为浏览器会优化连续的DOM操作,所以其实不过会时有发生同差的页面重排出现,优化后底代码如下:

`var newWidth = div1.offsetWidth + 10;
var newHeight = myElement.offsetHeight + 10;

div1.style.width = newWidth + ‘px’;
myElement.style.height = newHeight + ‘px’;`

  1. 用事件托管方绑定事件

以DOM元素上绑定事件会潜移默化页面的属性,一方面,绑定事件本身会占处理时,另一方面,浏览器保存事件绑定,所以绑定事件呢会见占有内存。页面中
元素绑定的风波越多,占用的拍卖时以及内存就一发充分,性能也不怕相对更差,所以于页面中绑定的风波更少越好。一个淡雅的一手是运事件托管方,即采用事件冒
泡机制,只在父元素上绑定事件处理,用于拍卖所有子元素的波,在事件处理函数中因传入的参数判断事件源元素,针对不同的源元素做不同的拍卖。这样即便非
需要被每个子元素都绑定事件了,管理的轩然大波绑定数量变少了,自然特性为就是增强了。这种艺术也出充分酷之油滑,可以老有利地增长或去子元素,不待考虑为
元素移除或改动而需要改事件绑定。示例代码如下:

// 获取父节点,并添加一个click事件
document.getElementById('list').addEventListener("click",function(e) { // 检查事件源元素 if(e.target && e.target.nodeName.toUpperCase
== "LI") { // 针对子元素的处理 ...
    }
});

上述代码中,只在父元素上绑定了click事件,当接触击子节点时,click事件会冒泡,父节点捕获事件后透过e.target检查事件源元素并做相应地拍卖。

当JavaScript中,事件绑定方式是浏览器兼容问题,所以于成千上万框架中吗提供了一般之接口方法用于事件托管。比如当jQuery中得以下如下方式贯彻事件之托管(示例代码来jQuery官方网站):

$( "table" ).on( "click", "td", function() {
$( this ).toggleClass( "chosen" );

});

初稿地址: http://developer.51cto.com/art/201504/473422.htm