本文参考: 《高性能JS》、网页性能管理详解javascript
浏览器下载完页面中的全部组件--HTML标记、JS、CSS、图片--以后会解析并生成两个内部数据结构:css
"生成布局"(flow)和"绘制"(paint)这两步,合称为"渲染"(render)。html
网页生成的时候,至少会渲染一次。用户访问的过程当中,还会不断从新渲染。java
DOM 树种的每个须要显示的节点在渲染树中至少存在一个对应的节点(隐藏的DOM元素在渲染树中没有对应的节点)。web
渲染树中的节点称为“帧(frames)”或“盒(boxes)”,符合CSS模型的定义。chrome
重排是什么:从新生成布局。当DOM 的变化影响了元素的几何属性(宽和高)--好比改变边框宽度或给段落增长文字致使行数增长--浏览器须要从新计算元素的几何属性,一样其余元素的几何属性和位置也会所以受到影响。浏览器会使渲染树中受到影响的部分失效,并从新构造渲染树。这个过程称为重排。浏览器
重绘是什么:从新绘制。完成重排后,浏览器会从新绘制受影响的部分到屏幕中。这个过程称为重绘。数据结构
重排必定会致使重绘,重绘不必定致使重排。若是DOM变化不影响几何属性,元素的布局没有改变,则只发生一次重绘(不须要重排)。app
当页面布局和几何属性改变时发生“重排”。以下:函数
整个页面或局部。例如:当滚动条出现时触发整个页面的重排。
重排和重绘会不断触发,这是不可避免的。可是,它们很是耗费资源,是致使网页性能低下的根本缘由。
提升网页性能,就是要下降"重排"和"重绘"的频率和成本,尽可能少触发从新渲染。
前面提到,DOM变更和样式变更,都会触发从新渲染。可是,浏览器已经很智能了,会尽可能把全部的变更集中在一块儿,排成一个队列,而后一次性执行,尽可能避免屡次从新渲染。
div.style.color = 'blue';
div.style.marginTop = '30px';
复制代码
上面代码中,div元素有两个样式变更,可是浏览器只会触发一次重排和重绘。
若是写得很差,就会触发两次重排和重绘。
div.style.color = 'blue';
var margin = parseInt(div.style.marginTop);
div.style.marginTop = (margin + 10) + 'px';
复制代码
上面代码对div元素设置背景色之后,第二行要求浏览器给出该元素的位置,因此浏览器不得不当即重排。
获取布局信息的操做会致使列队刷新,如下属性和方法须要返回最新的布局信息,最好避免使用。
offsetTop
, offsetLeft
, offsetWidth
, offsetHeight
scrollTop
, scrollLeft
, scrollWidth
, scrollHeight
clientTop
, clientLeft
, clientWidth
, clientHeight
getComputedStyle()
(currentStyle in IE)clientTop:元素上边框的厚度,当没有指定边框厚底时,通常为0。
scrollTop:位于对象最顶端和窗口中可见内容的最顶端之间的距离,简单地说就是滚动后被隐藏的高度。
offsetTop:获取对象相对于由offsetParent属性指定的父坐标(css定位的元素或body元素)距离顶端的高度。
clientHeight:内容可视区域的高度,也就是说页面浏览器中能够看到内容的这个区域的高度,通常是最后一个工具条如下到状态栏以上的这个区域,与页面内容无关。
scrollHeight:IE、Opera 认为 scrollHeight 是网页内容实际高度,能够小于 clientHeight。FF 认为 scrollHeight 是网页内容高度,不过最小值是 clientHeight。
offsetHeight:获取对象相对于由offsetParent属性指定的父坐标(css定位的元素或body元素)的高度。IE、Opera 认为 offsetHeight = clientHeight + 滚动条 + 边框。FF 认为 offsetHeight 是网页内容实际高度,能够小于clientHeight。offsetHeight在新版本的FF和IE中是同样的,表示网页的高度,与滚动条无关,chrome中不包括滚动条。
Window.getComputedStyle()方法返回一个对象,该对象在应用活动样式表并解析这些值可能包含的任何基本计算后报告元素的全部CSS属性的值。 私有的CSS属性值能够经过对象提供的API或经过简单地使用CSS属性名称进行索引来访问。
因此,从性能角度考虑,尽可能不要把读操做和写操做,放在一个语句里面。
// bad
div.style.left = div.offsetLeft + 10 + "px";
div.style.top = div.offsetTop + 10 + "px";
// good
var left = div.offsetLeft;
var top = div.offsetTop;
div.style.left = left + 10 + "px";
div.style.top = top + 10 + "px";
复制代码
通常的规则是:
当查询布局信息时,好比获取偏移量(offset)、滚动位置(scroll)或计算出的样式值(computedstyle values)时,浏览器为了返回最新值,会刷新队列并应用全部变动。不利于优化。
因此应该尽可能减小布局信息的获取次数,获取后把它赋值给局部变量,而后再操做局部变量
// 优化前
myElement.style.left = 1 + myElement.offsetLeft + 'px';
myElement.style.top = 1 + myElement.offsetTop + 'px';
if (myElement.offsetLeft >= 500) {
stopAnimation();
}
// 优化后
// 获取一次起始位置的值,而后赋值给一个变量,在动画循环中直接使用变量再也不查询偏移量
var current = myElement.offsetLeft;
current++;
myElement.style.left = current + 'px';
myElement.style.top = current + 'px';
if (myElement.offsetLeft >= 500) {
stopAnimation();
}
复制代码
cssText
属性如今大部分浏览器都自动优化了
// 优化前
var el = document.getElementById('mydiv');
el.style.borderLeft = '1px';
el.style.borderRight = '2px';
el.style.padding = '5px';
// 优化后
var el = document.getElementById('mydiv');
el.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px;';
复制代码
class
名称而不是修改内联样式var el = document.getElementById('mydiv');
el.className = "active";
复制代码
var ul = document.getElementById('mylist');
ul.style.display = 'none';
appendDataToElement(ul, data); // 更新指定节点数据的函数
ul.style.display = 'block';
复制代码
文档片断是个轻量级的document对象,用于更新和移动节点。当你附加一个片断到节点中,实际上添加的是该片断的子节点,而不是片断自己。
该方法产生的DOM遍历和重排次数最少。
//建立一个文档片断
var fragment = document.createDocumentFragment();
// 更新文档片断的数据
appendDataToElement(fragment, data);
// 将文档片断附加到原始列表中(实际添加的是子节点)
document.getElementById('mylist').appendChild(fragment);
复制代码
实例以下:
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>使用fragment进行重排重绘</title> </head> <body> <ul id="myList"> <li>a</li> <li>b</li> </ul> <p> 向上面的ul中加入两个新的li,比较使用fragment和不使用的性能 </p> <script> console.time(0); var newLi1 = document.createElement('li'); newLi1.innerHTML = 'c'; var newLi2 = document.createElement('li'); newLi2.innerHTML = 'd'; document.getElementById('myList').appendChild(newLi1); document.getElementById('myList').appendChild(newLi2); console.timeEnd(0) console.time(1); var fragment = document.createDocumentFragment(); var newLi1 = document.createElement('li'); newLi1.innerHTML = 'c'; var newLi2 = document.createElement('li'); newLi2.innerHTML = 'd'; fragment.appendChild(newLi1); fragment.appendChild(newLi2); document.getElementById('myList').appendChild(fragment); console.timeEnd(1) </script> </body> </html> 复制代码
var old = document.getElementById('mylist');
// 对旧节点备份
var clone = old.cloneNode(true);
appendDataToElement(clone, data);
// 用副本节点代替旧节点
old.parentNode.replaceChild(clone, old);
复制代码
许多展开区域的几何动画会将页面其余部分推向下方。通常来讲,重排只影响渲染树中的一部分,可是也可能影响很大的部分。
当页面顶部的一个动画推移页面整个余下的部分时,会致使一次代价昂贵的大规模重排。
使用如下步骤能够避免页面中的大部分重排: