重排重绘与帧优化管理

浏览器渲染网页的过程

  1. 首先渲染引擎将 HTML 解析成 DOM 树;
  2. 再将 CSS 代码解析成 CSSOM(CSS Object Model);
  3. 结合 DOM 和 CSSOM 生成渲染树。通过 CSSOM 中的 class,id 等属性将对应的样式挂载到 DOM Tree 上。(即对每个节点描述视觉信息);
  4. 根据渲染树生成页面布局。即将渲染树的视觉信息进行平面合成; - layout
  5. 将布局绘制到屏幕上。- paint

重排和重绘

网页生成,至少需要渲染一次。用户访问的过程中,还会不断的重新渲染。
由于浏览器采用流式布局模型(Flow Based Layout),对渲染树布局计算通常只需要遍历一次就能完成。但对于 table 及其内部的子元素,可能需要多次计算,这是为什么要避免使用 table 布局的主要原因。

重新渲染就是重新生成布局和重新绘制

以下行为都会导致重新渲染

  • 修改 dom(增、删、改 dom 节点)
  • 修改样式表
  • 用户事件(比如鼠标悬停、页面滚动、输入框键入文字、改变窗口大小等)
  • 重排 当节点的几何尺寸发生变化,布局需要重新计算时,也就触发了重排。
  • 重绘 相反,如果节点只是样式发生改变,则触发重绘

重绘不一定会触发重排,重排必然会导致重绘。

减少重排和重绘,优化性能

频繁的重排和重绘,以及部分的重排是导致页面性能低下的主要原因。那么减少重排、重绘及合理的重排至关重要。
如果修改某个深层的节点,它并不会对其他节点造成影响。但修改一个页面顶级或嵌套多层节点的节点时,会影响整个页面的改变,付出的性能代价也是巨大的。

浏览器有一个比较只能的策略,基于你的脚本会创建一个变化队列,浏览器不断向队列添加变更的节点状态,然后一次执行,尽量减少重新渲染。

但是你的脚本也会让浏览器立即执行重新渲染,一般来说样式的写操作之后,如果有下面的这些读操作,会立即引起浏览器的重新渲染:

1
2
3
4
offsetTop/offsetLeft/offsetWidth/offsetHeight
scrollTop/scrollLeft/scrollWidth/scrollHeight
clientTop/clientLeft/clientWidth/clientHeight
getComputedStyle()

所以从性能角度考虑,尽量不要吧读操作和写操作放在一个语句里

1
2
3
4
5
6
7
8
9
// 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";

帧管理

网页动画的每一帧都是一次重新渲染,一般网页动画,需要达到每秒30-60帧才能比较顺畅。如果要达到动画最佳的流畅状态,每次重新渲染的时间不能超过16.66ms。