前言
最近一直在看八股文,发现不管是框架还是HTML部分,都免不了要涉及到浏览器的渲染问题,那么这里就涉及到重绘-Redraw
、重排-Reflow
(回流,个人觉得更符合的翻译是重排)两个经典的面试题。
浏览器渲染原理
根据下图, 可知道浏览器渲染分以下步骤:
- 浏览器到HTML文件,解析HTML文件生成DOM树;;
- 处理CSS标记,构建层叠样式模型CSSOM(CSS Object Model);
- 将DOM树与CSSOM合并为渲染树(rendering tree);
- 渲染树进行布局。浏览器会对渲染树每个元素包含的内容进行计算,并通过一种流式的方式处理,只需要一次pass绘制操作布局所有元素;
- 将渲染树的各个节点绘制到屏幕上-(painting)
什么是重排?
当元素布局属性发生改变,需要重新计算元素在页面中的布局位置时,浏览器重新进行布局的过程。例如:修改元素的宽度、高度、位置等。
什么是重绘?
当元素的样式发生改变,不影响布局的情况下,浏览器重新绘制元素的过程。例如修改元素的背景颜色、文字颜色等。
为什么推荐使用重绘的方式,减少使用重排?
从浏览器渲染原理图中,我们不难发现重排发生在Layout布局阶段,这样就会额外消耗性能去重新计算元素位置和页面布局,而重绘则只需要绘制已经计算好布局的元素样式。
如何减少重排?
- **使用CSS动画替代:**CSS动画是利用的GPU加速,在性能方面通常比JavaScript动画更高效。使用CSS的
transform
和opacity
属性来创建动画效果,而不是改变元素的布局属性。 - **使用
translate3d
开启硬件加速:**将元素的位移属性设置为translate3d(0, 0, 0)
,可以强制使用GPU加速,有助于避免回流,并提高动画的流畅性。 - **避免频繁操作影响布局的样式属性:**当需要对元素进行多次样式修改时,可以考虑将这些修改合并为一次操作。通过添加/移除CSS类样式一次性改变多个样式属性,而不是逐个修改。
- 灵活运用
requestAnimationFrame
:通过使用requestAnimationFrame
方法调度动画帧,可以确保动画在浏览器的重绘周期执行,从而避免不必要的回流。这种方式可以确保动画在最佳时间节点进行渲染。 - **使用文档片段(Document Fragment):**当需要向DOM中插入大量新元素时,可以先将元素添加到文档片段中,最后将整个文档片段一次性注入DOM中,这样可以减少回流次数。例如:vue的虚拟dom(vNode);
- **让元素脱离文档流:**使用
position
的absolute
、fixed
和float,这样可以减少回流。
流:**使用position
的absolute
、fixed
和float,这样可以减少回流。 - **CSS属性控制:**使用
visibility: hidden
替代display: none
,visibility: hidden
不会触发回流,仍然占据空间,只是不可见;而display: none
则会将元素从渲染树中移除,引起回流。