前端优化-Javascript篇(4.DOM优化)

上篇我引见了Javascript标识符查找方面的优化,能够看出在这方面的优化给机能带来的提拔并不显著,以至能够说基础没有影响。然则,我本日要分享的是前端Javascript优化的一个大头。尽人皆知,在浏览器端Javascript中DOM操纵比拟平常Javascript代码来讲是比较耗时的,所以在DOM优化高低工夫能够收到相称可观的机能优化。下面我将分享几个DOM方面的机能优化战略。

耗时的DOM操纵

浏览器中的Javascript能够分为两个部份:ECMAScript和DOM API。而比拟原生的ECMAScript来讲,DOM API会耗时许多。我们能够把这两部份设想成两个经由历程桥梁衔接的小岛,在ECMAScript小岛上举行的操纵运转速率比在DOM小岛上面的操纵要快许多,每次在举行DOM操纵的时刻你都须要从ECMAScript这个小岛经由历程这个桥梁抵达DOM小岛上然后在上面举行耗时的操纵。所以大批的DOM操纵就会下降机能。
人人先看看下面这个例子:

//优化前
var start = new Date().getTime() ;
for(var i = 0 ; i < length ; i ++){
    document.getElementById("test").innerHTML += "a" ;
}
console.log("Before:" + (new Date().getTime() - start)) ;
//优化后
start = new Date().getTime() ;
var content = "" ;
for(var i = 0 ; i < length ; i ++){
    content += "a" ;
}
document.getElementById("test").innerHTML += content ;
console.log("After:" + (new Date().getTime() - start)) ;

从运转效果来看,能够说差别那是相称显著啊:

《前端优化-Javascript篇(4.DOM优化)》

优化前的代码每一次轮回都举行了DOM操纵,而优化以后,只在末了一步举行了DOM操纵,这就是DOM优化的气力啊。所以,我们应当在操纵的时刻只管防止对DOM的操纵,能少操纵DOM就少操纵。依据上面的比方就好比是,我们经由历程桥梁从ECMAScript小岛抵达DOM小岛,然后找出须要举行操纵的元素,把它再带回到ECMAScript小岛举行操纵,经由历程这个体式格局,能够加速操纵的速率,我们应当只管多的把元素带回到ECMAScript小岛举行操纵。

innerHTML照样createElement

在页面上动态增加结点平常有两个要领:innerHTML和createElement要领。这两个要领在机能上也有一点差别,详细差别在哪儿呢?上代码:

var start = new Date().getTime() ;

var content = "<div>" ;
for(var i = 0 ; i < 1000 ; i ++){
    content += "<div></div>" ;
}
content += "</div>" ;
document.getElementById("test").innerHTML += content ;

console.log("innerHTML:" + (new Date().getTime() - start)) ;

document.getElementById("test").innerHTML = "" ;

start = new Date().getTime() ;
//为了防止直接往test节点上面增加节点引发的页面重画,所以运用一个div节点来存储增加的节点,末了把div增加到页面中
var div = document.createElement("div") ;
for(var i = 0 ; i < 1000 ; i ++){
    div.appendChild(document.createElement("div")) ;
}
document.getElementById("test").appendChild(div) ;

console.log("createElement:" + (new Date().getTime() - start)) ;

这段代码在差别浏览器上的运转效果是不一样的:

《前端优化-Javascript篇(4.DOM优化)》

在Chrome上createElement比innerHTML快,而在Firefoxhe和IE上效果则相反,从效果上看似乎是innerHTML以2:1赢了,但是我照样发起人人运用createElement,我把上面的代码改成下面如许:

var start = new Date().getTime() ;
var test = document.getElementById("test") ;

for(var i = 0 ; i < 1000 ; i ++){
    test.innerHTML += "<div></div>" ;
}

console.log("innerHTML:" + (new Date().getTime() - start)) ;
document.getElementById("test").innerHTML = "" ;

start = new Date().getTime() ;

for(var i = 0 ; i < 1000 ; i ++){
    test.appendChild(document.createElement("div")) ;
}

console.log("createElement:" + (new Date().getTime() - start)) ;

上面这段代码的运转效果

《前端优化-Javascript篇(4.DOM优化)》

能够看出来innerHTML和createElement差许多。为了测试我用了比较大的数据1000,在现实开辟中平常不会涌现这类状况,所以机能上的差别也就不会那末显著,然则除了斟酌机能题目之外,我们还应当斟酌代码的可读性以及可保护下方面的题目,而斟酌到这些的话,我个人照样比较引荐运用createElement,假如人人有什么别的意见,迎接一同议论。

HTMLCollection

HTMLCollection是若干个DOM节点的鸠合,它具有数组的一些特征,比方length属性、经由历程下标接见,然则它并非数组,它没有push和slice要领。在DOM操纵中我们常常会用到HTMLCollection,下面的要领都邑返回HTMLCollection:

  • getElementsByName

  • getElementsByTagName

  • getElementsByClassName

  • document.forms

  • document.images

  • document.links

另有一些别的要领和属性会返回HTMLCollection,在这里就不一一列举了。怎样处置惩罚它们也是影响机能的一个方面。优化战略跟上面的迥然差别,就是用局部变量缓存鸠合以及鸠合的长度,我就不举行现实测试了。HTMLCollection另有一个很主要的特征就是它是依据页面的状况动态更新的,假如你更新的页面那末它的内容也会发作变化。比方下面这段代码:

var divs = document.getElementsByTagName("div") ;
for(var i = 0 ; i < divs.length ; i ++){
    document.body.appendChild(document.createElement("div")) ;
}

这段代码的原意是向body中增加多一倍的div节点,然则真正的运转会致使死轮回,这就是由于divs是动态更新的,每次向body中增加div节点都邑使length属性发作变化也就是加1,所以这个轮回会一向实行下去,在开辟的时刻应当注重这个题目。一个抱负的方法就是缓存divs的长度,如许就不会引发死轮回了。

节点挑选

假如须要获得某个节点的所以孩子节点,我们可能会用到childNodes属性;获得第一个孩子,我们可能会用到firstChild;获得下一个兄弟节点,我们可能会用到nextSibling。然则这些属性都存在一些题目就是它们会把一些空格和空行也看成孩子节点返回给我们,而这些常常不是我们所想要的,假如运用这些属性那末我们就须要对它们举行挑选,如许势必会影响效力。所以我们应当用别的属性来替代这些,看下表:

《前端优化-Javascript篇(4.DOM优化)》

表格左侧的是引荐的属性,它们只会返回Element节点。不过并非一切浏览器都支撑,所以在运用之前我们须要先推断一下。

运用选择器要领替代传统要领

当代浏览器给我们供应了别的一种要领在猎取我们须要的节点,这个要领是querySelectorAll和querySelector。它们经由历程CSS选择器作为参数,返回满足前提的节点。querySelectorAll要领返回满足前提的一切节点而querySelector返回满足前提的第一个节点。运用这两个要领来替代我们之前常经常使用的getElementById,getElementsByTagName等要领也是进步机能的一个门路。不过照样老题目,并非一切浏览器都支撑这两个要领,一切照样先做个推断吧。

Reflow 和 Repaint

起首,Repaint是指页面上的元素的表面发作了转变然则不影响规划的状况下引发的浏览器从新绘画元素表面的行动,比方修正color,background-color等属性。Reflow是指页面上的元素的大小规划发作的变化从而引发浏览器对页面其他元素位置大小举行从新盘算而且规划的行动。Reflow所致使的机能斲丧远比Repaint大,所以我们下面重点议论Reflow状况下的优化战略。
在议论Reflow之前先简朴的看一下浏览器加载页面的历程。如下图:

《前端优化-Javascript篇(4.DOM优化)》

浏览器在收到HTML文档以后对其举行剖析,剖析历程分为两个部份DOM文档的剖析和CSS款式的剖析。剖析DOM文档天生一个DOM树,DOM树和剖析出来的CSS款式组合天生一个衬着树,末了浏览器依据这个衬着树举行页面的排版和绘画。而末了这一步就是会涉及到Reflow和Repaint。
以下这几个行动会引发页面的Reflow或Repaint:

  1. 增加,删除,更新DOM节点

  2. 隐蔽/显现DOM节点(display:none或visibility:hidden)

  3. 修正款式

  4. 转变窗口大小,转动页面

实在浏览器在这方面已帮我们做了一些优化了,关于每一个触发Reflow的行动浏览器并不会立时就触发,而是把它们保存在一个行列中,当抵达肯定数目的时刻再举行批量的Reflow,如许就不须要每次都举行Reflow。然则,我们的一些行动会影响到浏览器的优化,使得Reflow立时触发。当我们要求下面这些属性的时刻发作这类征象:

  1. offsetTop, offsetLeft, offsetWidth, offsetHeight

  2. scrollTop/Left/Width/Height

  3. clientTop/Left/Width/Height

  4. getComputedStyle(), or currentStyle(IE)

每当我们要求这些属性时,浏览器为了返回及时的状况就必须立时举行Reflow以盘算出我们所须要的属性。所以我们应当只管少的运用这些属性。
从上面能够发明,基于一切DOM操纵都邑引发Reflow或Repaint,所以只管防止页面的Reflow或Repaint能够很好的进步DOM机能。那末该怎么做才最好的防止或最小化Reflow呢?下面有几个有效的发起:
1.不要一一修正款式,而改成经由历程修正className来批量转变款式,假如款式须要动态盘算,那末也要运用cssText属性来批量增加款式。比方:

// 毛病的做法
var left = 10,
    top = 10;
el.style.left = left + "px";
el.style.top  = top  + "px";
 
// 运用修正className来举行优化
el.className += " theclassname";
 
// 假如须要动态修正css,那末就运用cssText
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";

2.批量处置惩罚DOM操纵而且让元素离开文档流,等操纵完毕后再放回文档流中。有以下几种方法:

  • 运用display:none隐蔽element,然后举行操纵,末了再显现出来

  • 运用documentFragment ,把新增的节点放在documentFragment中,末了再把documentFragment放到DOM中,由于把documentFragment放到DOM中,它只会把它的孩子节点放到DOM中,就好像documentFragment不存在。

  • 经由历程cloneNode复制节点,然后离线举行操纵,末了再替代DOM中的节点。

3.只管少的接见会引发立时Reflow的属性,运用局部变量来缓存这些属性,比方:

var left = el.offsetLeft,
    top  = el.offsetTop
    esty = el.style;
for(big; loop; here) {
    left += 10;
    top  += 10;
    esty.left = left + "px";
    esty.top  = top  + "px";
}

4.关于须要动画的元素,只管让它离开文档流,如许就可以只管引发只管小的Reflow

5.只管少运用table规划

事宜代办

事宜代办我想这个人人应当都知道了。越多的事宜绑定页面就加载越慢而且占用更多内存,同时绑定太多事宜也会使得代码的可读性下降。运用事宜代办的要领道理就是把事宜绑定到元素的父节点,然后在处置惩罚函数中推断target,依据差别的target实行差别的逻辑。如许能很大水平的削减绑定是事宜数目而且进步代码的简约度。

总结

看了这么多实在总结起来照样比较简朴的,在举行DOM操纵的时刻只管把DOM操纵转换为当地的Javascript操纵,运用时先缓存一些DOM元素或许属性,缓存长度。在须要举行大批DOM操纵的时刻,先让元素离开文档,等操纵完毕再把元素放回文档中。优化战略照样须要在实践中不停尝试,不停探索,找出最优的解决方案。

近来预备毕设没什么时候更新博客,背面只管安排好时候做到一周一篇,前端优化Javascript篇未完待续。。。

原文地点:
http://lakb248.github.io/2014/06/13/optimization_of_front-end–javascript(4optimization_of_dom)/

末了,安利下我的个人博客,迎接接见: http://bin-playground.top

    原文作者:lakb248
    原文地址: https://segmentfault.com/a/1190000000579452
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞