DOM操作优化

文档对象模型 (DOM)是一个独立于特定语言的应用程序接口。

1:在浏览器中,DOM是以javascript实现的,通过javascript来操作浏览器页面的元素。

2:DOM提供了丰富的接口,但是DOM操作代价很高。

3:页面前端代码的性能瓶颈大多集中在DOM操作上。

4:前端性能优化主要关注点是DOM操作优化。

5:DOM操作优化总原则是尽量减少DOM操作。

DOM操作为什么会影响性能。

1:DOM的实现和ECMAscript的实现是分离的。  

--IE中,ECMAscript的实现在jscript.dll中,而DOM的实现在mshtml.dll中

--Chrome中,ECMAscript的实现在V8引擎中,而DOM的实现在webkit中的webCore。

其他浏览器类似,所以通过javascript 代码调用DOM借口,相当于两个独立模块的交互,相比较在同一模块的调用,这种跨模块调用性能损耗很高。(但是DOM操作对性能影响最大的还是浏览器的repaint 和reflow 重绘和重排)。

简述下浏览器渲染原理:从下载文档到渲染页面的过程中

1:浏览器会通过解析HTML文档来构建DOM树

2:解析CSS产生CSS规则树

3:解析js代码,这可能会修改生成的DOM树和CSS规则树

4:根据DOM树和CSS树构建渲染树(css会根据选择器匹配HTML元素)

5:渲染树包含每个元素的大小,边距等样式属性,不包含隐藏元素,head等不可见元素。

6:浏览器根据元素的坐标和大小来计算每个元素的位置,并绘制这些元素到页面上。

7:重绘是指页面的某些部分要重新绘制,比如颜色或者背景色,但是元素的位置和尺寸并没改变。

8:重排是指元素的位置或者尺寸发生改变,浏览器需要重新计算渲染树,导致渲染树一部分或者全部发生变化。渲染树重新建立后,浏览器会重新绘制页面上受影响的元素。

9:重排比重绘的代价高很多。

现代浏览器会针对重绘和重排做性能优化,比如,把DOM操作积累一批后统一做一次重排或重绘。但有些情况浏览器会立即重排或重绘,比如:请求DOM元素布局信息:

offsetTop/Left/Width/Height、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、getComputedStyle()、currentStyle.这些值都是动态计算的,所以浏览器需要尽快完成页面的绘制,然后计算返回值,从而打乱了重排或重绘的优化。

重排和重绘是不可避免的,但是可以降低他们带来的影响:

1: 合并多次DOM操作。

1 for(...){
2   document.getElementById('ele').innerHTML="a_string";
3 }
4 
5 for(...){
6    arr[].push 
7 }

2:把DOM元素离线或者隐藏后修改。(适合需要那些大批量修改DOM元素的情况)

(2.1):使用文档片段。

  --文档片段是一个轻量级的document对象。

       --不会和特定页面关联。

  --通过在文档片段上进行DOM操作可降低DOM操作对页面性能影响。

  --操作完成后附加在页面中。

  --对页面性能影响只存在于最后把文档片段附加到页面这一步操作上。

1 var fragment = document.createDocumentFragment();
2 //基于fragment的大量DOM操作
3 document.getElementById('myEle').appendChild(fragment);

(2.2):设置DOM元素的display样式为none来隐藏元素。

  --通过隐藏DOM元素,达到在页面中移除元素的效果。

  --经过大量的DOM操作后恢复元素原来的display样式。

1 var myEle = document.getElementById("myEle");
2 myEle.style.display="none";
3 //基于myEle的大量的DOM操作
4 myEle.style.display="block";

(2.3):克隆DOM元素到内存中。

  --把页面的DOM元素克隆一份到内存中。

  --再在内存中操作克隆的元素。

  --操作完成后用此克隆的元素替代页面中原来的DOM元素。

1 var old = document.getElementById('myEle');
2 var clone = old.cloneNode(true);
3 //一些基于clone的大量DOM操作
4 old.parentNode.prelaceChild(clone,old);

现代浏览器中因为有DOM操作优化,所以应用如上方式可能不能明显感受到性能改善,但是仍有市场的旧浏览器中,应用以上者三种编码方式可大幅提高页面渲染性能。

3:设置具有动画效果的DOM元素的position属性为fixed或absolute。

  --设置动画效果的元素为绝对定位

  --使元素脱离页面布局流

  --避免频繁的重排,只涉及动画元素自身的重排。

  --可提高动画效果的展示性能。

 如果把动画设为绝对定位不符合要求,那可以在动画开始时设为绝对定位,等动画结束后恢复原始的定位设置。

4:谨慎取得DOM元素的布局信息。

  --获取DOM元素的布局信息会强制浏览器刷新渲染树,并可能会导致页面的重排或重绘。

  --如果需要这些布局信息,最好是在DOM操作之前就取得。

1 var newWidth = div1.offsetWidth+10;
2 div1.style.width = newWidth +'px';
3 
4 var newHeight = div2.offsetHeight + 10;
5 div2.style.Height = newHeight +'px';

代码在遇到取得DOM元素信息时会触发页面重新计算渲染树,所以上边代码会导致页面重排两次,如果把取得的DOM元素布局信息提前,因为浏览器会优化连续的DOM操作,所以实际上只会有一次的页面重排出现。

1 var newWidth=div1.offsetWidth+10;
2 var newHeight = div2.offsetHeight+10;
3 
4 div1.style.width = newWidth+"px";
5 div2.style.height = newHeight + "px";

5:事件托管的方式来绑定事件。

  --DOM元素绑定事件会影响页面性能。一方面,绑定事件本身会占用处理时间,另一方面,浏览器保存事件绑定会占用内存。

  --页面元素绑定的事件越多,占用的处理时间和内存就越大。性能越差。

  --页面绑定的事件越少越好。

  --优雅的解决方法是事件托管方法(利用事件冒泡机制,只在父元素上绑定事件处理,用于所有子元素的事件,在事件处理函数中根据传入的参数判断事件源元素,针对不同的源元素做不同的处理,这样就不需要给每个子元素都绑定事件了。)

  --可以很方便的添加或删除子元素,不需要考虑因子元素移除或改动而需要修改事件绑定。

1 document.getElementById('list').addEventListener("click", function(e){
2      //检查事件源元素
3      if(e.target && e.target.nodeName.toUpperCase() == "LI"){
4          //针对子元素的处理
       console.log("clicked"+e.target.innerHTML);
5 } 6 })

上边的代码里,只在父元素绑定了click事件。当点击子节点时,click事件会冒泡,父节点捕捉事件后通过e.target检查事件源元素并做相应处理。但是事件绑定方式存在浏览器兼容问题。所以在很多框架提供了接口方法用于事件托管。比如 Jquery

1 $("table").on("click","td",function(){
2      $(this).toggleClass("chosen");
3 });
原文地址:https://www.cnblogs.com/ming-os9/p/8932370.html