原文链接:https://segmentfault.com/a/1190000000490322

缓存DOM对象

JavaScript的DOM操作可以说是JavaScript最重要的功能,我们经常要根据用户的操作来动态的增加和删除元素,或是通过AJAX返回的数据动态生成元素。比如我们获得了一个很多元素的数组data[],需要将其每个值生成一个li元素插入到一个id为container的ul元素中,最简单(最慢)的方式是:

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()),在内存中进行操作后再替换原来的节点

一次性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给元素增加样式,比如如下代码:

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元素上的事件处理就行了

前端性能优化(DOM篇)的更多相关文章

  1. 前端性能优化JavaScript篇

    关于前端性能优化的讨论一直都很多,包罗的知识也很多,可以说性能优化只有更好,没有最好.前面我写了一篇关于css优化的总结文章,今天再从javascript方面聊一聊. 1.从资源加载方面来说,浏览器的 ...

  2. 前端性能优化---缓存篇SDK

    1.把前端最常用的资源css.js存在本地1.1  前端缓存技术SessionStorage 优点:临时存储神器,关闭页面标签自动回收,不可以跨页面交互. 取值的时候有两种方法,一种是用session ...

  3. Web前端性能优化进阶——完结篇

    前言 在之前的文章 如何优化网站性能,提高页面加载速度 中,我们简单介绍了网站性能优化的重要性以及几种网站性能优化的方法(没有看过的可以狂戳 链接 移步过去看一下),那么今天我们深入讨论如何进一步优化 ...

  4. 前端性能优化(JavaScript篇)

    正巧看到在送书,于是乎找了找自己博客上记录过的一些东西来及其无耻的蹭书了~~~ 小广告:更多内容可以看我的博客 优化循环 如果现在有个一个data[]数组,需要对其进行遍历,应当怎么做?最简单的代码是 ...

  5. 前端性能优化--为什么DOM操作慢? 浅谈DOM的操作以及性能优化问题-重绘重排 为什么要减少DOM操作 为什么要减少操作DOM

    前端性能优化--为什么DOM操作慢?   作为一个前端,不能不考虑性能问题.对于大多数前端来说,性能优化的方法可能包括以下这些: 减少HTTP请求(合并css.js,雪碧图/base64图片) 压缩( ...

  6. CSS3与页面布局学习总结(八)——浏览器兼容与前端性能优化

    一.浏览器兼容 1.1.概要 世界上没有任何一个浏览器是一样的,同样的代码在不一样的浏览器上运行就存在兼容性问题.不同浏览器其内核亦不尽相同,相同内核的版本不同,相同版本的内核浏览器品牌不一样,各种运 ...

  7. CSS3与页面布局学习笔记(八)——浏览器兼容性问题与前端性能优化方案

    一.浏览器兼容 1.1.概要 世界上没有任何一个浏览器是一样的,同样的代码在不一样的浏览器上运行就存在兼容性问题.不同浏览器其内核亦不尽相同,相同内核的版本不同,相同版本的内核浏览器品牌不一样,各种运 ...

  8. Web前端性能优化全攻略

    网页制作poluoluo文章简介:Web 前端性能优化是个大话题,是个值得运维人员持续跟踪的话题,是被很多网站无情忽视的技术. Web 前端性能优化是个大话题,是个值得运维人员持续跟踪的话题,是被很多 ...

  9. Web前端性能优化全攻略[转载]

    1. 尽量减少 HTTP 请求 (Make Fewer HTTP Requests) 作为第一条,可能也是最重要的一条.根据 Yahoo! 研究团队的数据分析,有很大一部分用户访问会因为这一条而取得最 ...

  10. HTML5前端性能优化——浏览器兼容与前端性能优化

    一.浏览器兼容 1.1.概要 世界上没有任何一个浏览器是一样的,同样的代码在不一样的浏览器上运行就存在兼容性问题.不同浏览器其内核亦不尽相同,相同内核的版本不同,相同版本的内核浏览器品牌不一样,各种运 ...

随机推荐

  1. 【C语言入门教程】5.6 函数库和文件

    函数库是为代码复用建立的,将同一类型,需要在不同的程序里使用的函数放置在一起,就组成了一个函数库.如 C 语言的标准库,它集合了开发者常用的函数.开发者自行编写的函数也可以组成函数库,通常称之为自定义 ...

  2. 转载自安卓巴士 【收藏】2015必须推荐的Android框架,猿必读系列!

    一.Guava Google的基于java1.6的类库集合的扩展项目,包括collections, caching, primitives support, concurrency libraries ...

  3. java自定义标签 权限

    <?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java ...

  4. 跟着百度学PHP[4]OOP面对对象编程-17-多态

    多态是除封装和继承之外的另一个面象对象的三大特性之一. 多态的作用简而言之就是为程序做括展. 比如说在公司上班,每个月财务发放工资,同一个发工资的方法,在公司内不同的员工或是不同职位的员工,都是通过这 ...

  5. POJ 2195 Going Home 最小费用最大流 尼玛,心累

    D - Going Home Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Subm ...

  6. Python PEP8规范

    转载自:http://blog.csdn.net/kellyseeme/article/details/50644893 风格指南:http://zh-google-styleguide.readth ...

  7. centos mysqldb 安装

    yum install mysql-devel.x86_64 yum install python-devel pip install MySQL-python

  8. 【自动部署】Ansible 怎么通过堡垒机/跳板机 访问目标机器

    Ansible机器的 /root/.ssh/config 配置如下即可:Host 目标机器IP User root IdentityFile=/root/.ssh/xxx_id_rsa ProxyCo ...

  9. ios awakeFromNib 和 initWithCoder:

    During the instantiation process, each object in the archive is unarchived and then initialized with ...

  10. iis 错误解决汇集

    1. Windows7,VS2013,网站发布到IIS,访问发生如下错误: HTTP 错误 500.21 - Internal Server Error处理程序“NickLeeCallbackHand ...