开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 19 天,点击查看活动详情
重绘Repaint 和回流 Reflow ,在性能优化方面经常被提及到,它们会对页面的渲染速度产生影响。
想要了解重绘和回流,首先需要知道浏览器的渲染原理。
浏览器的渲染
在 CSS 和 JS 加载完成后,会进行 DOM 渲染和 Render 树渲染
-
获取 HTML ,解析为 DOM 树
-
解析 CSSOM ,形成一个 CSSOM
-
将 DOM 和 CSSOM 合并成 Render 树(渲染树)
-
进行布局(遍历渲染树的每个节点,计算节点的大小、位置等)
-
绘制节点
重绘
重绘是指元素外观的改变所触发的浏览器行为。节点上元素的位置、大小等不发生变化,而是颜色、字体大小等改变时,浏览器会将新的样式重新绘制一遍。
触发重绘的操作:
- color,background-color 等。
- visibility (节点隐藏,但位置不变)
注意:table 及其内部元素可能需要多次计算才能确定好其在渲染树中节点的属性值, 比同等元素要多花两倍时间,这就是我们尽量避免使用 table 布局页面的原因之一。
回流
回流,也可以叫做重排/重构。当渲染树中的一部分因为元素的大小,布局,隐藏等改变而需要重新构建, 这个过程就称为回流。
触发回流的操作:
- 每个页面至少需要一次回流(页面首次加载的时候)
- 元素的大小、位置改变
- 浏览器窗口尺寸改变
- 操作 DOM (添加、删除...)
- CSS 伪类(:hover 等)被激活
重绘和回流的关系
- 回流必定会引发重绘,但重绘不一定会引发回流(在回流的时候,浏览器会重新构造受到影响的部分渲染树,完成回流后,重新绘制受影响的部分到屏幕中。)
- 回流的开销较大,而重绘消耗较少
浏览器对重绘回流的优化
浏览器会将修改的操作放入队列里,,才清空队列。需要注意的是:当执行一些获取布局信息的 API 的时候,会强制刷新队列(这种情况可以先缓存起来)
如何减少重绘和重排
- 不在布局信息改变时做 DOM 查询
- 使用 class 一次性改变样式
- 减少使用 table 布局
- 减少使用 Css 表达式(如: calc())
- 对于多次重排的元素,比如动画,使用绝对定位脱离文档流(这样不影响其他元素)
- DocumentFragment 页面添加大量的元素会导致大量的重绘以及回流,DocumentFragment 可以创建缓存区来保存节点,然后再一次性添加到父节点中。
requestAnimationFrame
requestAnimationFrame() 是浏览器专门为动画提供的 API。告诉浏览器执行动画,并在下一次重绘之前调用指定的函数来更新动画。
- requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来,不需要像 setTimeout 一样设置时间间隔。
- 当页面不是激活状态时,动画会自动暂停,节省了 CPU 开销。
- 在隐藏或不可见的元素中,requestAnimationFrame 不会进行重绘或回流