DOM树:表示页面结构。
渲染树:表示DOM节点如何显示。
定义
当DOM元素影响了元素的几何属性(例如宽和高),浏览器需要重新计算元素的几何属性,同样其它元素的几何属性也会和位置也会因此受到影响。浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。这个过程称为“重排”。
完成重排后,浏览器会重新绘制受影响的部分到屏幕上中,该过程称为“重绘”。
- 重排发生的情况:
- 添加或删除可见的DOM元素。
- 元素位置改变。
- 元素的尺寸改变(包括:内外边距、边框厚度、宽度、高度等属性的改变)。
- 内容改变。
- 页面渲染器初始化。
- 浏览器窗口尺寸改变。
- 重绘发生的情况:
重绘发生在元素的可见的外观被改变,但并没有影响到布局的时候。比如,仅修改DOM元素的字体颜色(只有Repaint,因为不需要调整布局)
优化办法
-
fragment元素的应用
<ul id='fruit'>
<li> apple </li>
<li> orange </li>
</ul>
如果代码中要添加内容为peach、watermelon两个选项,你会怎么做?
传统做法:
var lis = document.getElementById('fruit');
var li = document.createElement('li');
li.innerHTML = 'peach';
lis.appendChild(li);var li = document.createElement('li'); li.innerHTML = 'watermelon'; lis.appendChild(li);
很容易想到如上代码,但是很显然,重排了两次。隐藏的元素不在渲染树中,太棒了,我们可以先把id为fruit的ul元素隐藏,然后添加li元素,最后再显示,但是实际操作中可能会出现闪动,原因这也很容易理解。这时,fragment元素就有了用武之地了。
var fragment = document.createDocumentFragment();
var li = document.createElement('li');
li.innerHTML = 'peach';
fragment.appendChild(li);
var li = document.createElement('li');
li.innerHTML = 'watermelon';
fragment.appendChild(li);
document.getElementById('fruit').appendChild(fragment);
文档片段的一个便利的语法特性是当你附加一个片断到节点时,实际上被添加的是该片断的子节点,而不是片断本身。只触发了一次重排,而且只访问了一次实时的DOM。
- 缓存布局信息
例如myElement元素沿对角线移动,每次移动一个像素。到500*500像素的位置结束。timeout循环体中可以这么做
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();
} - 让元素脱离文档流
使用绝对位置定位页面上的动画元素,将其脱离文档流。触发一小区域的重绘。