推荐答案
重绘 (Repaint):当元素的外观(如颜色、背景色、字体颜色等)发生改变,但不影响其在文档流中的位置和布局时,浏览器会进行重绘。重绘只需要重新绘制元素,不会引起页面布局的改变。
回流/重排 (Reflow):当元素的尺寸、位置、结构等发生改变,影响到其他元素的布局时,浏览器需要重新计算元素的几何属性,重新构建渲染树,这个过程称为回流或重排。回流会触发重绘。
触发重绘的情况:
- 修改元素的颜色,背景色,字体颜色等
- 修改元素的透明度
- 添加或删除元素的 box-shadow,outline 等
- visibility 属性改变
触发回流的情况:
- 页面初次渲染
- 窗口大小发生改变
- 元素的位置、尺寸发生变化(例如:width、height、margin、padding、border)
- 元素的内容发生变化(例如:文本改变或图片被替换)
- 添加或删除可见的DOM元素
- 激活 CSS 伪类(例如 :hover)
- 查询某些属性或者调用某些方法,例如:offsetTop/Left/Width/Height, clientTop/Left/Width/Height, scrollTop/Left/Width/Height, getComputedStyle(), getBoundingClientRect()等。
- 修改 CSS 样式时,需要计算才会生效的样式,如 float、position
减少重绘和回流的方法:
- 集中修改样式: 尽量使用 class 批量修改样式,避免逐条修改。
- 使用transform和opacity进行动画: 这些属性的改变不会触发回流,只会触发重绘,性能更高。
- 避免频繁操作DOM: 可以先将DOM元素脱离文档流,修改后再重新插入。
- 避免使用 table 布局: table 的布局计算复杂,容易导致回流。
- 使用
requestAnimationFrame
进行动画: 保证浏览器在合适的时机进行渲染,减少不必要的计算。 - 使用
documentFragment
进行批量DOM操作: 将多个DOM操作添加到 fragment 中,然后一次性插入到文档中。 - 避免强制同步布局: 在修改DOM后立即获取布局信息(例如:offsetTop)。
- 缓存布局信息: 在需要多次用到布局信息时,先将其缓存,避免重复计算。
- 使用 CSS Containment 属性: 将部分内容隔离,可以减少回流影响范围。
本题详细解读
重绘 (Repaint) 和回流/重排 (Reflow) 的概念
重绘 (Repaint)
重绘是指当元素的视觉样式(例如:颜色、背景色、字体颜色)发生改变,但元素的布局(即元素在页面上的位置和大小)没有发生改变时,浏览器会进行的重新绘制元素的过程。
重绘是一个相对较轻的操作,因为它不需要重新计算元素的几何属性和布局,只需要重新绘制元素即可。这就像是在已经画好的画布上重新着色。
回流/重排 (Reflow)
回流(也称为重排)是指当元素的尺寸、位置、结构等发生改变,导致其在文档流中的位置和大小发生变化,进而影响到其他元素的布局时,浏览器需要重新计算所有受到影响的元素的几何属性(包括位置、大小等),重新构建渲染树的过程。
回流是一个更重的操作,因为它需要重新计算元素的几何属性和布局,而且回流通常会触发重绘。这就像是在画布上重新调整元素的位置和大小,然后再重新着色。
触发重绘和回流的情况详解
触发重绘的情况
- 修改元素的颜色属性: 例如:
color
、background-color
、border-color
等。 - 修改元素的透明度: 例如:
opacity
属性。 - 修改阴影和轮廓: 例如:
box-shadow
、outline
等。 - 修改
visibility
属性: 当visibility
的值在visible
和hidden
之间切换时,也会触发重绘。
触发回流的情况
- 页面初次渲染: 当页面加载完成,浏览器需要计算所有元素的布局。
- 窗口大小变化: 当用户调整浏览器窗口的大小时,会触发回流以适应新的窗口大小。
- 元素的位置和尺寸变化: 包括
width
、height
、margin
、padding
、border
、top
、left
、right
、bottom
等属性的改变。 - 元素的内容变化: 元素的文本内容改变、图片被替换等会改变元素的尺寸,从而触发回流。
- 添加或删除DOM元素: 当有新的 DOM 元素被添加或者有 DOM 元素被删除时,浏览器会重新计算布局。
- 激活 CSS 伪类: 例如
:hover
伪类,当鼠标悬停在元素上时可能会触发回流。 - 查询某些属性或方法: 比如,
offsetTop/Left/Width/Height
,clientTop/Left/Width/Height
,scrollTop/Left/Width/Height
,getComputedStyle()
,getBoundingClientRect()
这些操作会触发浏览器立即进行布局计算,因为它们需要元素的最新布局信息。 - 改变
font-size
,font-family
等会影响元素大小的样式 - 添加
float
,position
属性
如何减少重绘和回流
理解了重绘和回流的原理,我们就可以有针对性地优化性能。
- 集中修改样式: 尽量使用
class
来批量修改样式,避免逐条修改元素的style
属性,从而减少回流次数。例如,可以预先定义好一些样式类,然后一次性添加到元素上,而不是多次修改元素的样式。 - 使用
transform
和opacity
进行动画:transform
和opacity
属性的改变不会触发回流,只会触发重绘。使用这些属性进行动画可以显著提高动画的性能。例如使用transform: translate()
来移动元素,而不是修改元素的top
或left
。 - 避免频繁操作DOM: 尽量减少直接操作 DOM 的次数。可以先将 DOM 元素脱离文档流(例如:将元素的
display
属性设置为none
,或者使用documentFragment
),进行批量修改,然后再重新插入到文档流中。 - 避免使用 table 布局:
table
的布局计算复杂,任何对table
的修改都可能会导致整个table
的回流。尽量使用div
等其他元素进行布局。 - 使用
requestAnimationFrame
进行动画:requestAnimationFrame
方法告诉浏览器在下一次重绘之前调用指定的函数。这样可以确保动画在合适的时机进行,从而减少不必要的计算和回流。 - 使用
documentFragment
进行批量 DOM 操作: 可以将多个 DOM 操作添加到documentFragment
中,然后一次性插入到文档中,这样可以减少回流的次数。 - 避免强制同步布局: 在修改DOM后立即获取布局信息(例如:
offsetTop
)。这样会导致浏览器被迫进行回流操作。尽量先批量修改 DOM,然后统一获取布局信息。 - 缓存布局信息: 如果需要多次使用某个元素的布局信息(例如:
offsetWidth
,offsetHeight
),可以将这些信息缓存起来,避免重复计算。 - 使用 CSS Containment 属性: 可以使用
contain
属性将元素的渲染独立出来,从而减少回流的影响范围。例如使用contain: layout
可以限制回流只在该元素内发生。
通过以上优化手段,可以有效地减少重绘和回流的发生,从而提高页面的性能和用户体验。