概念:
重排(reflow):重新生成布局
重绘(repaint):重新绘制
"重绘"不一定需要"重排",比如改变某个网页元素的颜色,就只会触发"重绘",不会触发"重排",因为布局没有改变。但是,"重排"必然导致"重绘"。
九个技巧:
第一条,DOM 的多个读操作(或多个写操作),应该放在一起。不要两个读操作之间,加入一个写操作。如:
// bad
div.style.color = 'blue';
var margin = parseInt(div.style.marginTop);
div.style.marginTop = (margin + 10) + 'px';
第二条,如果某个样式是通过重排得到的,那么最好缓存结果。避免下一次用到的时候,浏览器又要重排。
第三条,不要一条条地改变样式,而要通过改变class,或者csstext属性,一次性地改变样式。
// bad
var left = 10;
var top = 10;
el.style.left = left + "px";
el.style.top = top + "px";
// good
el.className += " theclassname";
// good
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
第四条,尽量使用离线DOM,而不是真实的网面DOM,来改变元素样式。比如,操作Document Fragment对象,完成后再把这个对象加入DOM。再比如,使用 cloneNode() 方法,在克隆的节点上进行操作,然后再用克隆的节点替换原始节点。
Document Fragment 使用方法:
假设我们要往ul里面插入li元素
// Create the fragment
var frag = document.createDocumentFragment();
//DocumentFragment实际上像一个伪DOM节点,在这个例子里你可以把它当成虚拟的UL元素。
// Create numerous list items, add to fragment
for(var i = 0; i < 10; i++) {
var li = document.createElement("li");
li.innerHTML = "List item " + i;
frag.appendChild(li);
}
//然后把DocumentFragement挂到它的父节点上
ul.appendChild(frag);
第五条,先将元素设为display: none(需要1次重排和重绘),然后对这个节点进行100次操作,最后再恢复显示(需要1次重排和重绘)。这样一来,你就用两次重新渲染,取代了可能高达100次的重新渲染。
第六条,position属性为absolute或fixed的元素,重排的开销会比较小,因为不用考虑它对其他元素的影响。
第七条,只在必要的时候,才将元素的display属性为可见,因为不可见的元素不影响重排和重绘。另外,visibility : hidden的元素只对重绘有影响,不影响重排。
第八条,使用虚拟DOM的脚本库,比如React等。
第九条,使用 window.requestAnimationFrame()、window.requestIdleCallback() 这两个方法调节重新渲染。
window.requestAnimationFrame():它可以将某些代码放到下一次重新渲染时执行。
function doubleHeight(element) {
var currentHeight = element.clientHeight;
element.style.height = (currentHeight * 2) + 'px';
}
elements.forEach(doubleHeight);
上面的代码使用循环操作,将每个元素的高度都增加一倍。可是,每次循环都是,读操作后面跟着一个写操作。这会在短时间内触发大量的重新渲染,显然对于网页性能很不利。
我们可以使用window.requestAnimationFrame(),让读操作和写操作分离,把所有的写操作放到下一次重新渲染。
function doubleHeight(element) {
var currentHeight = element.clientHeight;
window.requestAnimationFrame(function () {
element.style.height = (currentHeight * 2) + 'px';
});
}
elements.forEach(doubleHeight);
页面滚动事件(scroll)的监听函数,就很适合用 window.requestAnimationFrame() ,推迟到下一次重新渲染。
$(window).on('scroll', function() {
window.requestAnimationFrame(scrollHandler);
});
当然,最适用的场合还是网页动画。下面是一个旋转动画的例子,元素每一帧旋转1度。
var rAF = window.requestAnimationFrame;
var degrees = 0;
function update() {
div.style.transform = "rotate(" + degrees + "deg)";
console.log('updated to degrees ' + degrees);
degrees = degrees + 1;
rAF(update);
}
rAF(update);
window.requestIdleCallback():它指定只有当一帧的末尾有空闲时间,才会执行回调函数。
这里暂不介绍,如有兴趣,可以看本文的原文地址