关于浏览器的重绘回流

175 阅读2分钟

一、浏览器渲染过程

  • 解析HTML,生成DOM树,解析CSS,生成CSSOM树;
  • 将DOM树和CSSOM树结合,生成渲染树(Render Tree);
    • 从DOM树的根节点开始遍历每个可见节点;(不可见节点有script,meta,link,display:none等)
    • 对于每个可见的节点,找到CSSOM树中对应的规则,并应用它们;
    • 根据每个可见节点以及其对应的样式,组合生成渲染树。
  • Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小);
  • Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素;
  • Display:将像素发送给GPU,展示在页面上。

二、回流

2.1 回流主要是计算节点在设备视口(viewport)内的确切位置和大小,当页面布局和几何信息发生变化的时候就触发回流,有以下情况:

  • 添加或删除可见的DOM元素;
  • 元素的位置发生变化;
  • 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等);
  • 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代;
  • 页面一开始渲染的时候;
  • 浏览器的窗口尺寸变化;
  • 获取布局信息的操作的时候(如offsetTop、offsetLeft、offsetWidth、offsetHeight、 scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight、getComputedStyle()、getBoundingClientRect)。

三、重绘

将节点绘制为屏幕上的真实像素显示出来,有如下情况:

  • 颜色的修改;
  • 文本方向的修改;
  • 阴影的修改。

注意:回流一定会触发重绘,而重绘不一定会回流(需要改变屏幕显示的,但又不需要回流重新计算大小位置改变布局的)

四、减少回流和重绘的措施

4.1 合并多次样式的修改,然后一次处理掉

例如有:

const element = document.getElementById('div1');
element.style.padding = '5px';
element.style.borderRight = '5px';

上面每一个修改都会触发回流,如果是旧版浏览器就会触发2次重绘,而现代浏览器一般都做了优化,自动合并只触发一次重绘。 解决措施:

const element = document.getElementById('div1');
element.style.cssText += 'padding: 5px;border-right: 5px;';

或者用工具类修改(我个人喜欢这种方式)

const element = document.getElementById('div1');
element.className += ' active';
4.2 批量修改DOM
  • 先设置display:none,修改完毕再换回display:block;
  • document.createDocumentFragment;
4.3 缓存获取布局信息的操作
4.4 复杂动画效果使用绝对定位,让它脱离文档流;
4.5 css3硬件加速(GPU加速)

CSS 中的以下几个属性能触发硬件加速:

  • transform
  • opacity
  • filter
  • will-change

参考资料:

你真的了解回流和重绘吗--腾讯IVWEB团队