重绘和重排(回流)
重绘不一定导致重排,但重排一定会导致重绘 重排(Reflow) && 重绘(Redraw)会付出高昂的性能代价
重排(Reflow): 重排是指浏览器需要重新计算元素的位置和大小,然后重新绘制整个页面的过程。这可能由于以下原因触发:
- 添加、删除或改变 DOM 元素。
- 改变窗口大小。
- 修改元素的样式,如改变宽度、高度、边距、填充、边框等。
- 获取某些元素的布局信息(如宽度、高度、位置)。
重排是非常昂贵的操作,因为它会导致整个页面重新布局和绘制,对性能产生负面影响。
重绘(Repaint): 重绘是指在不影响页面布局的情况下,改变元素的外观样式而引起的页面重新绘制。这包括修改元素的颜色、背景、字体等属性,而不会导致元素的位置和大小发 生变化。重绘比重排开销小,但仍然会消耗一定的性能资源。
浏览器的工作流程
解析HTML和构建DOM树:浏览器首先解析HTML,构建DOM(文档对象模型)树。DOM树表示页面的结构,每个元素都是树中的一个节点。
解析CSS和构建CSSOM树:接下来,浏览器解析CSS样式表,并构建CSSOM(CSS对象模型)树。CSSOM树表示页面的样式信息,每个CSS规则都是树中的一个节点。
合并DOM树和CSSOM树,构建渲染树:浏览器将DOM树和CSSOM树合并成一个渲染树(Render Tree)。渲染树只包含需要渲染的可见元素,不包括不可见的元素(例如 display: none 的元素)。
布局(回流):一旦有了渲染树,浏览器就需要计算每个元素在屏幕上的确切位置和大小,这个过程叫做布局或回流。它会确定元素的位置、尺寸和相互关系。
绘制(重绘):一旦布局完成,浏览器就可以进行绘制或重绘操作。这时候,浏览器知道每个元素在屏幕上的准确位置和外观,可以开始将它们绘制到屏幕上。
合成与显示:最后,浏览器将渲染树的内容进行合成,并显示在屏幕上,呈现给用户。
造成重绘
重绘(Repaint)是在不改变元素布局的情况下,修改元素外观样式而引发的页面重新绘制。以下是一些可能触发重绘的动作:
-
修改元素的样式属性: 修改元素的颜色、背景、字体、文本颜色等样式属性。
-
添加、删除或更改类名: 当您通过JavaScript修改元素的类名时,可能会导致重绘,特别是在修改了影响样式的类名时。
-
修改伪类状态: 例如,当使用
:hover
、:active
或:focus
伪类选择器时,鼠标悬停在元素上或元素获得焦点时,可能会触发重绘。 -
改变文本内容: 修改元素的文本内容(不是文本样式)也可能引发重绘。
-
改变背景图像: 当您修改元素的背景图像或相关属性时,会触发重绘。
-
添加或删除伪元素(::before 和 ::after): 添加或删除伪元素可能会导致重绘,因为它们可以包含样式属性。
-
使用 CSS 动画和过渡: 使用 CSS 动画和过渡会导致元素的重绘,但通常不会引发重排。
-
滚动页面: 滚动页面时,页面上的元素可能会因显示或隐藏而触发重绘。
-
修改字体属性: 修改元素的字体属性(如字体大小、字体样式)可能导致重绘。
需要注意的是,重绘虽然比重排开销小,但仍然会消耗性能资源。因此,为了提高页面性能,应尽量减少不必要的重绘操作,特别是在需要频繁更新元素样式的情况下。优化页面的渲染性能是前端开发中重要的任务之一。
造成重排
重排(Reflow)是浏览器中一种昂贵的操作,它会重新计算元素的位置和大小,然后更新页面的布局。以下是一些可能触发重排的动作:
-
添加、删除或改变 DOM 元素结构: 任何更改 DOM 结构的操作都可能引发重排,包括添加、删除、移动、更改元素的属性等。
-
修改元素的布局属性: 修改元素的样式属性,如宽度、高度、内边距、外边距、边框等,通常会引发重排。
-
获取元素的布局信息: 使用 JavaScript 获取元素的布局信息,如
offsetWidth
、offsetHeight
、clientWidth
、clientHeight
等,会强制浏览器重新计算布局。 -
改变窗口大小: 当用户改变浏览器窗口大小时,页面的布局会发生变化,因此会触发重排。
-
滚动页面: 如果用户滚动页面,页面上的元素可能会因显示或隐藏而触发重排。
-
改变字体大小: 修改字体大小可能会影响整个文档的布局,因此会触发重排。
-
激活 CSS 伪类: 例如,使用
:hover
、:active
、:focus
伪类选择器会改变元素的状态,可能触发重排。 -
更改表单元素: 用户与表单元素交互,如输入文本、选择选项、勾选复选框等,可能会导致页面重新布局,从而触发重排。
-
调整浏览器的默认字体: 一些浏览器在首次渲染页面时需要获取默认字体信息,如果修改了默认字体,可能会导致重排。
-
动画操作: 使用 JavaScript 或 CSS 动画会频繁地修改元素的样式,这可能触发多次重排。
需要注意的是,重排是非常昂贵的操作,它会导致整个页面重新布局和绘制,对性能产生负面影响。因此,在开发中应该尽量避免不必要的重排操作,合理优化页面结构和样式,以提高页面 的性能。
如何避免重绘和重排提高性能
要避免重绘和重排以提高性能,可以采取以下一些最佳实践:
-
使用 CSS3 动画和过渡: CSS3 提供了
transform
和opacity
属性,它们通常会触发重绘而不会引发重排。如果需要创建动画效果,请尽量使用 CSS3 动画和过渡,而不是使用 JavaScript 操作 DOM。 -
使用离屏渲染: 对于复杂的动画或渲染效果,可以考虑使用离屏渲染技术。这将元素绘制到一个不可见的缓冲区中,然后再将结果绘制到页面上。这可以减少页面的重绘开销,但需要小心使用,因为它可能会增加内存占用。
-
使用硬件加速: 某些属性(如
transform
和opacity
)可以触发浏览器的硬件加速,从而减少重绘的成本。确保您的动画或效果使用了硬件加速。 -
最小化样式变化: 修改样式属性时,尽量避免改变元素的布局。这包括尽量不修改宽度、高度、边距、填充等属性,因为这些变化可能导致重排。如果需要改变布局,最好一次性应用所有变化,而不是逐个修改。
-
使用
will-change
属性:will-change
属性可以告诉浏览器哪些属性将会发生变化,从而优化渲染过程。例如,可以使用will-change: transform;
来提示浏览器元素将进行变换。 -
避免频繁查询布局信息: 尽量不要使用会触发布局的属性( 如
offsetWidth
、offsetHeight
)来获取元素的布局信息。如果需要多次查询,最好将结果缓存起来,以减少性能开销。 -
使用缓存: 对于需要多次计算的值,可以将结果缓存起来,避免重复计算。这在计算复杂布局时特别有用。
-
使用
requestAnimationFrame
: 如果您需要执行动画效果,可以使用requestAnimationFrame
来优化动画的性能。它会在下一帧绘制之前调用,有助于避免不必要的重绘。
通过遵循这些最佳实践,可以减少页面的重绘和重排次数,从而提高页面的性能和响应速度。选择合适的方法取决于您的具体应用场景和需求。