重绘与回流or重排

178 阅读4分钟

回流or重排?

'回流和重排两个说法,其实都是一个意思'

通常来说,渲染引擎会解析HTML文档来构建DOM树,与此同时,渲染引擎也会用CSS解析器解析CSS文档构建CSSOM树。接下来,DOM树和CSSOM树关联起来构成渲染树(RenderTree),这一过程称为Attachment。然后浏览器按照渲染树进行布局(Layout),最后一步通过绘制显示出整个页面。
详细图:

1.png 当render tree(渲染树)中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建, 这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘。

注意:回流必将引起重绘,而重绘不一定会引起回流。回流会导致渲染树需要重新计算,开销比重绘大,所以我们要尽量避免回流的产生

回流的产生:

  1. 页面第一次渲染 在页面发生首次渲染的时候,所有组件都要进行首次布局,这是开销最大的一次回流。
  2. 浏览器窗口尺寸改变
  3. 元素位置和尺寸发生改变的时候
  4. 新增和删除可见元素
  5. 内容发生改变(文字数量或图片大小等等)
  6. 元素字体大小变化。
  7. 激活CSS伪类(例如::hover)。
  8. 设置style属性
  9. 查询某些属性或调用某些方法。比如说:
  10. offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight
  11. 除此之外,当我们调用getComputedStyle方法,或者IE里的currentStyle时,也会触发回流,原理是一样的,都为求一个“即时性”和“准确性”。

重绘的产生:

当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格 而不会影响布局的,比如visibility、outline、背景色等属性的改变。

下面的代码是影响回流和重绘的:

var s = document.body.style;
s.padding = "2px"; // 回流+重绘
s.border = "1px solid red"; // 再一次 回流+重绘
s.color = "blue"; // 重绘
s.backgroundColor = "#ccc"; // 重绘
s.fontSize = "14px"; // 再一次 回流+重绘
document.body.appendChild(document.createTextNode('abc!'));// 添加node,再一次 回流+重绘

因为 JavaScript 可以同时修改 DOM 和 CSSOM。 由于浏览器不确定特定的JavaScript会做什么,所以它采取的预防措施是停止整个DOM构造,一般情况我们会将JavaScript 放到页面的底部,等页面要渲染结束再引进来,JavaScript是影响回流和重绘的关键。

常见的避免方法:

样式集中改变:通过class和cssText进行集中改变样式 未进行优化的代码是这样的:

var left = 10;
var top = 10;
el.style.left = left + "px";
el.style.top = top + "px";

虽然现在大部分现代浏览器都会有Flush队列进行渲染队列优化 但是有些老版本的浏览器比如IE6这样的坑货效率依然低下: 这时我们就可以通过上面所说的利用class和cssText属性集中改变样式

// 这样
el.className += " className";
// 或者这样
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";

缓存布局信息

// 修改div的位置 触发两次回流
div.style.left = div.offsetLeft + 1 + 'px';
div.style.top = div.offsetTop + 1 + 'px';
//  缓存位置信息  相当于读写分离
var curLeft = div.offsetLeft;
var curTop = div.offsetTop;
div.style.left = curLeft + 1 + 'px';
div.style.top = curTop + 1 + 'px';

将position属性设置为absolute或fixed
position属性为absolute或fixed的元素,回流开销比较小 不用考虑它对其他元素的影响

让要操作的元素进行”离线处理”
处理完后一起更新使用DocumentFragment进行缓存操作,引发一次回流和重绘; 可以参考这篇文章 了解DocumentFragment 给我们带来的性能优化使用display:none技术 只引发两次回流和重绘;前面也提到,如果影响到页面布局就会导致回流 那么先把元素设置为 display:none 最后再 display:block 即可使用cloneNode(true or false) 和 replaceChild 技术,引发一次回流和重绘