为什么说js操作DOM很慢

 

DOM

文档对象模型(Document Object Model简称DOM),是W3C组织推荐的处理扩展标记语言的标准编程接口。在网页上,组织页面(或文档)的对象被组织在一个树形结构中,用来表示文档中对象的标准模型就成为DOM。DOM的历史追溯至1990年代后期微软于Netscape的“浏览器大战”。双方为了在javascript和JScript一决生死,于是大规模的赋予浏览器强大的功能。微软在网页技术上计入了不少专属事物,既有VBScript,ActivityX,以及微软自家的DHTML格式等,使不少网页使用非微软平台及浏览器无法正常显示。DOM即是当时酝酿出来的杰作。

 

  优点和缺点

  DOM的优势主要在于易用性强,使用DOM时,将把所有的XML文档信息都存在于内存中,并且遍历简单,支持XPath,增强了易用性。

  DOM的缺点主要表现在效率低,内存占用量过高,对于大文件来说几乎不可能。另外效率低还表现在大量的消耗时间,因为使用DOM进行解析时,将文档的每个Element,Attribute,Processing-instruction和Comment都创建了一个对象,这样在DOM机制中运用了大量对象的创建和销毁无疑影响其效率。

 

为什么说js操作DOM会影响性能呢?

在浏览器中DOM得实现和ECMAScript是分离得。

在IE中EMCMAScript是实现在jscript.dll中,DOM实现在mshtml.dll中。在Chrome中使用Webkit中的WebCore处理DOM和渲染,ECMAScript是在V8引擎中实现的。其他浏览器也类似。

因此在使用js操作DOM的时候是通过js代码调用DOM的接口,这就相当于两个互相独立的模块发生交互,这样的性能损耗是非常高的。

然而影响DOM操作性能的主要原因是它会导致浏览器重绘和重排。

 

  浏览器渲染的原理

  我以前写过:站在浏览器角度谈前端优化

  简单理解为浏览器解析html形成DOM树,解析css形成CSSOM树,然后他两合并成渲染树。然后根据Layout布局映射到屏幕上。当页面发生重绘或者重排的话,浏览器会重新计算,消耗很多性能,

 

  重绘

  页面某些部分需要重新绘制,由于节点的几何属性发生改变或者由于样式发生变化。例如改变元素的背景,屏幕上的部分内容发生改变需要更新,发生重绘。发生重绘元素的位置和尺寸都不会变,

  重排

  元素的位置或者尺寸发生改变。浏览器会重新计算渲染树。因为位置和尺寸更改势必会影响整体的布局,这样损耗的性能比较高。分为部分重排和全部重排,应该避免全部重排。重排必会重绘。

 

优化方法

  (1)把DOM累计起来批量操作。offsetTop/Left/Width/HeightscrollTop/Left/Width/HeightclientTop/Left/Width/HeightgetComputedStyle()或 currentStyle因为这些值都是动态计算的,必然会打乱重绘和重排。

  (2)合并多次DOM操作为单次DOM操作

  例1:

  这样操作就会发生DOM重绘或重排3次;

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

  合并的话只操作一次;

element.style.cssText += 'border: 1px solid #f00;';

  例2:

  以前我经常这样动态插入列表

for (var i = 0; i < data.lengt; i++) {
    var html = `<li>${data[i].title}</li>`;
    $("ul").append(html);
}

  改为拼接字符串,最后插入DOM

var html = "";
for (var i = 0; i < data.lengt; i++) {
    html += `<li>${data[i].title}</li>`;
}
$("ul").append(html);

  (3)通过设置DOM元素的display样式为none来隐藏元素,这样引起的重排或重绘只是在显示和隐藏这两步

$("ul").css("display", "none");
for (var i = 0; i < data.lengt; i++) {
    var html = `<li>${data[i].title}</li>`;
    $("ul").append(html);
}
$("ul").css("display", "block");

  (4)克隆DOM到内存中,经过一系列操作,最后影响性能得就是最后一步了。

var yb_li = $("ul li").clone();
yb_li.text('yaobai');
yb_li.css('color', "red");
$("ul").append(yb_li);

  (5)设置具有动画效果得DOM元素的position属性为fixed或absolute。

  把页面具有动画效果的元素设置为绝对定位,使得元素脱离页面布局流,这样只涉及动画元素自身重排了,这种做法可以提高动画效果的展示性能。

  (6)缓存DOM对象,避免每次都从整个DOM树种查找

  不推荐

$('#mod .active').remove('active');
$('#mod .not-active').addClass('active');

  推荐

 

let $mod = $('#mod');
$mod.find('.active').remove('active');
$mod.find('.not-active').addClass('active');

 

posted on 2018-12-08 14:42  yaobai  阅读(1167)  评论(0编辑  收藏  举报

导航