程序员人生 网站导航

什么是 Repaint 和 Reflow, 相关介绍

栏目:jscript时间:2014-06-07 03:07:45

1. 什么是 repaint 和 reflow

一个页面由两部分组成:

DOM : 描述该页面的结构

render : 描述 DOM 节点 (nodes) 在页面上如何呈现

当 DOM 元素的属性发生变化 (如 color) 时, 浏览器会通知 render 重新描绘相应的元素, 此过程称为 repaint.

如果该次变化涉及元素布局 (如 width), 浏览器则抛弃原有属性, 重新计算并把结果传递给 render 以重新描绘页面元素, 此过程称为 reflow.

这两个过程是很耗费浏览器性能的, 从 IE 系列和 Chrome 渲染页面速度上的差距即可看出渲染引擎计算对应值和呈现并不一定高效, 而每次对元素的操作都会发生 repaints 或 reflow, 因此编写 DOM 交互时如果不注意就会导致页面性能低下.

2. Reflow

当发生以下情况的时候, 会引发 reflow:

可见元素的增删

元素的位置, 大小, 内容的改变

页面第一次渲染

改变浏览器窗口的大小

幸运地, 浏览器对 reflow 的处理是和 PHP 的 Output Control 相似的 — 把 reflow 放进一个队列, 达到一定程度或时限就进行 flush. 但不幸的是, 这个过程可能会被我们强制提前执行 — 只要使用下面所列的任一个都会迫使浏览器 flush, 因此产生 reflow:

offsetTop, offsetXXX…

scrollTop, scrollXXX…

clientTop, clientXXX…

getComputedStyle / currentStyle

3. 怎么优化

首先是减少对 DOM 中有关布局的操作, 假设我们要动态改变一个元素的大小为 100*100px:

var el = document.getElementById("id");
el.style.width = 100px;
el.style.height = 100px;
// suppose there are two els : el2, el3
el2.style.width = el.style.width * 1.5;
el3.style.width = el.style.width * 2;

写成这样会有更好的性能:

el.style.cssText = "width: 100px; height: 100px;";
// or el.className = "square";
var width = el.style.width;
el2.style.width = width * 1.5;
el3.style.width = width * 2;

操作 DOM 时尽量使用 DocumentFragment 和 cloneNode:

// DocumentFragment
var fm = document.createDocumentFragment();
var a;
for(var i = 0; i < 20; i++){
a = document.createElement("a");
a.href = i + ".html";
a.innerHTML = "page " + i;
fm.appendChild(a);
}
document.getElementById("tar").appendChild(fm);

// cloneNode
var ul = document.getElementById("ul");
var clone = ul.cloneNode(true); // true = deep copy
var li;
for(var i = 0; i < 20; i++){
li = document.createElement("li");
li.appendChild(document.createTextNode(i));
clone.appendChild(li);
}
ul.parentNode.replaceChild(clone, ul);

大致优化思路如下:

把元素从 DOM 流中剥离 (clone, fragment, position: absolute, 绝对定位对动画或拖曳对象尤为有用)

在被剥离的的元素上进行各种操作

把被剥离的元素恢复到 DOM 流

注意:

IE lte 8 有一个 bug — :hover, 大量出现使用该选择器的元素的话会降低页面响应速度

在不鸟 older browsers 的情况下, 可以大量使用 firstElementChild(),querySelectorAll() 等 API, 可实现的功能和 jQuery 等库的 CSS 选择器和遍历函数相差不大

此外对事件的绑定上尽可能使用 delegation, 也就是合理利用 event 的冒泡特性, 我认为这对 repaint & reflow 的影响是比较小的, 不过对于代码优化, 复用性和灵活性都有很大的帮助, 有空再继续

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐