浅谈DOM的操作以及性能优化问题-重绘重排


Posted in Javascript onJanuary 08, 2017

写在前面:

大家都知道DOM的操作很昂贵。 

然后贵在什么地方呢?

一、访问DOM元素

二、修改DOM引起的重绘重排

一、访问DOM

像书上的比喻:把DOM和JavaScript(这里指ECMScript)各自想象为一个岛屿,它们之间用收费桥梁连接,ECMAScript每次访问DOM,都要途径这座桥,并交纳“过桥费”,访问DOM的次数越多,费用也就越高。因此,推荐的做法是尽量减少过桥的次数,努力待在ECMAScript岛上。我们不可能不用DOM的接口,那么,怎样才能提高程序的效率?

既然无法避免,那就减少访问。(width、offsetTop、left。。。能少就少,可以缓存起来的,就缓存)

// code1错误
console.time(1);
for(var i = 0; i < times; i++) {
 document.getElementById('div1').innerHTML += 'a';
}
console.timeEnd(1);
 
// code2正确
console.time(2);
var str = '';
for(var i = 0; i < times; i++) {
 str += 'a';
}
document.getElementById('div2').innerHTML = str;
console.timeEnd(2);
////////////////////////

浅谈DOM的操作以及性能优化问题-重绘重排

html集合&遍历DOM

html集合类似数组,但是跟数组还是不一样的。如: document.getElementsByTagName('a') 返回的html集合。这个集合是实时更新的,即后面代码修改了DOM,会反映在这个html集合里面。可尝试代码。

<body>
 <ul id='fruit'>
 <li> apple </li>
 <li> orange </li>
 <li> banana </li>
 </ul>
</body>
<script type="text/javascript">
 var lis = document.getElementsByTagName('li');
 var peach = document.createElement('li');
 peach.innerHTML = 'peach';
 document.getElementById('fruit').appendChild(peach);
 
 console.log(lis.length); // 4
</script>

正因为这个原因:html集合,读取 length 属性比数组消耗大多了。

要解决这个问题并不难,在遍历DOM集合的时候,缓存length就好了。不要每次使用就获取,主要体现在for循环中(你应该知道,for循环中,每一次都会执行判读语句,读取length)

console.time(0);
var lis0 = document.getElementsByTagName('li');
var str0 = '';
for(var i = 0; i < lis0.length; i++) {
 str0 += lis0[i].innerHTML;
}
console.timeEnd(0);
 
console.time(1);
var lis1 = document.getElementsByTagName('li');
var str1 = '';
for(var i = 0, len = lis1.length; i < len; i++) {
 str1 += lis1[i].innerHTML;
}
console.timeEnd(1);

二、重绘重排

1.什么是重绘重排?

浏览器下载完页面中的所有组件——HTML标记、JavaScript、CSS、图片之后会解析生成两个内部数据结构——DOM树和渲染树。

在文档初次加载时,浏览器引擎通过解析 html文档 构建一棵DOM树,之后根据DOM元素的几何属性构建一棵用于展示渲染的渲染树。渲染树中的节点被称为“帧”或“盒",符合CSS模型的定义,可理解为(包括理解页面元素为一个具有大小,填充,边距,边框和位置的盒子)。由于隐藏元素不需要显示,渲染树中并不包含DOM树中隐藏的元素(知道这点有用)。 当渲染树构建完成,浏览器把每一个元素放到正确的位置上,然后再根据每一个元素的其他样式,绘制页面。

由于浏览器的流布局,对渲染树的计算通常只需要遍历一次就可以完成。但table及其内部元素除外,它可能需要多次计算才能确定好其在渲染树中节点的属性,通常要花3倍于同等元素的时间。这也是为什么我们要避免使用table做布局的一个原因。

重绘:是一个元素外观的改变所触发的浏览器行为,例如改变visibility、outline、背景色等属性(上面说到的其他属性)。浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。重绘不会带来重新布局,并不一定伴随重排。

重排:当DOM的变化影响了元素的几何属性(宽或高),浏览器需要重新计算元素的几何属性,同样其他元素的几何属性和位置也会因此受到影响。浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。这个过程称为重排。重排一定伴随着重绘。

2. 触发重排的操作:

2.1 修改DOM元素几何属性:

修改元素大小,位置,内容(一般只有重绘,但是内容可能导致元素大小变化)

2.2 DOM树结构发生变化

当DOM树的结构变化时,例如节点的增减、移动等,也会触发重排。浏览器引擎布局的过程,类似于树的前序遍历,是一个从上到下从左到右的过程。 通常在这个过程中,当前元素不会再影响其前面已经遍历过的元素。所以,如果在body最前面插入一个元素,会导致整个文档的重新渲染,而在其后插入一个元 素,则不会影响到前面的元素。

2.4 改变浏览器大小

3.渲染树变化的排队和刷新

思考下面代码:

var ele = document.getElementById('myDiv');
ele.style.borderLeft = '1px';
ele.style.borderRight = '2px';
// var _top = ele.offsetTop; //刷新队列
ele.style.padding = '5px';

三行代码,三次修改元素的几何属性,浏览器应该发生三次重排重绘。

但是浏览器并不会这么笨,它也是有做优化的。它会把三次修改“保存”起来(大多数浏览器通过队列化修改并批量执行来优化重排过程,也有设置时间片段的),一次完成!

然而,如果你在三行代码中,以下获取DOM布局信息。(为了返回最新的布局信息,将立即执行渲染树变化队列的更新)

如上面被注释的第4行,如果取消注释会导致(2+3)、(5)两次重排;

获取关于DOM布局信息的属性:

offsetTop, offsetLeft, offsetWidth, offsetHeight
scrollTop, scrollLeft, scrollWidth, scrollHeight
clientTop, clientLeft, clientWidth, clientHeight
getComputedStyle() (currentStyle in IE)

 

4 应对方法:尽量减少重绘次数、减少重排次数、缩小重排的影响范围。

4.1 合并多次操作,如上面的操作

ele.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px;';

4.2 将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位。

4.3 由于display属性为none的元素不在渲染树中,对隐藏的元素操作不会引发其他元素的重排。如果要对一个元素进行复杂的操作时,可以先隐藏它,操作完成后再显示。这样只在隐藏和显示时触发2次重排。但是这可能导致浏览器的闪烁。

4.4 在内存中多次操作节点,完成后再添加到文档中去(可使用fragment元素)。例如要异步获取表格数据,渲染到页面。可以先取得数据后在内存中构建整个表格的html片段,再一次性添加到文档中去,而不是循环添加每一行。

var fragment = document.createDocumentFragment();  // 未使用的虚拟节点,appendChild(fragment) //append的是里面的子元素
 
var li = document.createElement('li');
li.innerHTML = 'apple';
fragment.appendChild(li);
 
var li = document.createElement('li');
li.innerHTML = 'watermelon';
fragment.appendChild(li);
 
document.getElementById('fruit').appendChild(fragment);

以上就是小编为大家带来的浅谈DOM的操作以及性能优化问题-重绘重排全部内容了,希望大家多多支持三水点靠木~

Javascript 相关文章推荐
屏蔽F1~F12的快捷键的js函数
May 06 Javascript
对JavaScript客户端应用编程的一些建议
Jun 24 Javascript
JS实现文档加载完成后执行代码
Jul 09 Javascript
早该知道的7个JavaScript技巧
Jun 21 Javascript
js实现下拉菜单效果
Mar 01 Javascript
js 获取今天以及过去日期
Apr 11 Javascript
jQuery遍历节点方法汇总(推荐)
May 13 jQuery
jquery实现侧边栏左右伸缩效果的示例
Dec 19 jQuery
详解如何解决vue开发请求数据跨域的问题(基于浏览器的配置解决)
Nov 12 Javascript
深入浅析golang zap 日志库使用(含文件切割、分级别存储和全局使用等)
Feb 19 Javascript
微信小程序自定义联系人弹窗
May 26 Javascript
vue中实现图片压缩 file文件的方法
May 28 Javascript
完美解决node.js中使用https请求报CERT_UNTRUSTED的问题
Jan 08 #Javascript
jQuery+ajax的资源回收处理机制分析
Jan 07 #Javascript
JS实现iframe自适应高度的方法示例
Jan 07 #Javascript
JS小数转换为整数的方法分析
Jan 07 #Javascript
jQuery日程管理插件fullcalendar使用详解
Jan 07 #Javascript
基于jquery二维码生成插件qrcode
Jan 07 #Javascript
jquery validation验证表单插件
Jan 07 #Javascript
You might like
PHP.MVC的模板标签系统(三)
2006/09/05 PHP
用PHP查询搜索引擎排名位置的代码
2010/01/05 PHP
phpMyadmin 用户权限中英对照
2010/04/02 PHP
php实现的双色球算法示例
2017/06/20 PHP
PHP实现将标点符号正则替换为空格的方法
2017/08/09 PHP
详解PHP文件的自动加载(autoloading)
2018/02/04 PHP
JS 获取span标签中的值的代码 支持ie与firefox
2009/08/24 Javascript
使用jQuery模板来展现json数据的代码
2010/10/22 Javascript
javascript中attribute和property的区别详解
2014/06/05 Javascript
跟我学习javascript的作用域与作用域链
2015/11/19 Javascript
实例详解jQuery Mockjax 插件模拟 Ajax 请求
2016/01/12 Javascript
jquery实现点击弹出可放大居中及关闭的对话框(附demo源码下载)
2016/05/10 Javascript
AngularJS国际化详解及示例代码
2016/08/18 Javascript
js创建对象几种方式的优缺点对比
2016/09/28 Javascript
jQuery制作全屏宽度固定高度轮播图(实例讲解)
2017/07/08 jQuery
vue使用keep-alive实现数据缓存不刷新
2017/10/21 Javascript
详解如何在react中搭建d3力导向图
2018/01/12 Javascript
微信小程序模板消息限制实现无限制主动推送的示例代码
2019/08/27 Javascript
Vue3项目打包后部署到服务器 请求不到后台接口解决方法
2020/02/06 Javascript
python实现简单中文词频统计示例
2017/11/08 Python
Python实现的圆形绘制(画圆)示例
2018/01/31 Python
Python编程求质数实例代码
2018/01/31 Python
利用python实现短信和电话提醒功能的例子
2019/08/08 Python
Python OpenCV实现鼠标画框效果
2020/08/19 Python
keras模型可视化,层可视化及kernel可视化实例
2020/01/24 Python
Python基于requests库爬取网站信息
2020/03/02 Python
python3 通过 pybind11 使用Eigen加速代码的步骤详解
2020/12/07 Python
CSS3 画基本图形,圆形、椭圆形、三角形等
2016/09/20 HTML / CSS
优衣库台湾官网:UNIQLO台湾
2019/02/01 全球购物
某公司面试题
2012/03/05 面试题
财经学院自荐信范文
2014/02/02 职场文书
孝敬父母的活动方案
2014/08/28 职场文书
殡葬服务心得体会
2014/09/11 职场文书
标准离婚协议书范文下载
2014/11/30 职场文书
详解CSS故障艺术
2021/05/25 HTML / CSS
Python实现照片卡通化
2021/12/06 Python