一、浏览器渲染过程
- 解析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