前端性能优化(DOM操做篇)

正巧看到在送书,因而乎找了找本身博客上记录过的一些东西来及其无耻的蹭书了~~~javascript

小广告:更多内容能够看个人博客css

缓存DOM对象

JavaScript的DOM操做能够说是JavaScript最重要的功能,咱们常常要根据用户的操做来动态的增长和删除元素,或是经过AJAX返回的数据动态生成元素。好比咱们得到了一个不少元素的数组data[],须要将其每一个值生成一个li元素插入到一个id为container的ul元素中,最简单(最慢)的方式是:java

var liNode, i, m;
for (i = 0, m = data.length; i < m; i++) {
    liNode = document.createElement("li");
    liNode.innerText = data[i];
    document.getElementById("container").appendChild(liNode);
}

这里每一次循环都会去查找id为container的元素,效率天然很是低,因此咱们须要将元素在循环前查询完毕,在循环中仅仅是引用就好了,修改代码为:数组

var ulNode = document.getElementById("container");
var liNode, i, m;
for (i = 0, m = data.length; i < m; i++) {
    liNode = document.createElement("li");
    liNode.innerText = data[i];
    ulNode.appendChild(liNode);
}

缓存DOM对象的方式也常常被用在元素的查找中,查找元素应该是DOM操做中最频繁的操做了,其效率优化也是大头。在通常状况下,咱们会根据须要,将一些频繁被查找的元素缓存起来,在查找它或查找它的子孙元素时,以它为起点进行查找,就能提升查找效率了。浏览器

在内存中操做元素

因为DOM操做会致使浏览器的回流,回流须要花费大量的时间进行样式计算和节点重绘与渲染,因此应当尽可能减小回流次数。一种可靠的方法就是加入元素时不要修改页面上已经存在的元素,而是在内存中的节点进行大量的操做,最后再一并将修改运用到页面上。DOM操做自己提供一个建立内存节点片断的功能:document.createDocumentFragment(),咱们能够将其运用于上述代码中:缓存

var ulNode = document.getElementById("container");
var liNode, i, m;
var fragment = document.createDocumentFragment();
for (i = 0, m = data.length; i < m; i++) {
    liNode = document.createElement("li");
    liNode.innerText = data[i];
    fragment.appendChild(liNode);
}
ulNode.appendChild(fragment);

这样就只会触发一次回流,效率会获得很大的提高。若是须要对一个元素进行复杂的操做(删减、添加子节点),那么咱们应当先将元素从页面中移除,而后再对其进行操做,或者将其复制一个(cloneNode()),在内存中进行操做后再替换原来的节点app

一次性DOM节点生成

在这里咱们每次都须要生成节点(document.createElement("li")),而后将其加入到内存片断中,咱们能够经过innerHTML属性来一次性生成节点,具体的思路就是使用字符串拼接的方式,先生成相应的HTML字符串,最后一次性写入到ul的innerHTML中。修改代码为:函数

var ulNode = document.getElementById("container");
var fragmentHtml = "", i, m;
for (i = 0, m = data.length; i < m; i++) {
    fragmentHtml += "<li>" + data[i] + "</li>";
}
ulNode.innerHTML = fragmentHtml;

这样效率也会有提高,不过手动拼写字符串是至关麻烦的一件事优化

经过类修改样式

有时候咱们须要经过JavaScript给元素增长样式,好比以下代码:url

element.style.fontWeight = 'bold';
element.style.backgroundImage = 'url(back.gif)';
element.style.backgroundColor = 'white';
element.style.color = 'white';
//...

这样效率很低,每次修改style属性后都会触发元素的重绘,若是修改了的属性涉及大小和位置,将会致使回流。因此咱们应当尽可能避免屡次为一个元素设置style属性,应当经过给其添加新的CSS类,来修改其CSS

.element {
    background-image: url(back.gif);
    background-color: #fff;
    color: #fff;
    font-weight: 'bold';
    /*...*/
}
element.className += " element";

经过事件代理批量操做事件

仍是以前那个ul和添加li,若是咱们须要给每一个li都绑定一个click事件,就可能写出相似以下代码:

var ulNode = document.getElementById("container");
var fragment = document.createDocumentFragment();
var liNode, i, m;
var liFnCb = function(evt){
    //do something
};
for (i = 0, m = data.length; i < m; i++) {
    liNode = document.createElement("li");
    liNode.innerText = data[i];
    liNode.addEventListener("click", liFnCb, false);
    fragment.appendChild(liNode);
}
ulNode.appendChild(fragment);

这里每一个li元素都须要执行一次addEventListener()方法,若是li元素数量一多,就会下降效率。因此咱们能够经过事件代理的方式,将事件绑定在ul上,而后经过event.target来肯定被点击的元素是不是li元素,同时咱们也可使用innerHTML属性一次性建立节点了,修改代码为:

var ulNode = document.getElementById("container");
var fragmentHtml = "", i, m;
var liFnCb = function(evt){
    //do something
};
for (i = 0, m = data.length; i < m; i++) {
    fragmentHtml += "<li>" + data[i] + "</li>";
}
ulNode.innerHTML = fragmentHtml;
ulNode.addEventListener("click", function(evt){
    if(evt.target.tagName.toLowerCase() === 'li') {
        liFnCb.call(evt.target, evt);
    }
}, false);

这样事件绑定的代码就只要执行一次,能够监听全部li元素的事件了。固然若是须要移除事件回调函数,咱们也不须要循环遍历全部的li元素,只须要移除ul元素上的事件处理就好了

相关文章
相关标签/搜索