CSS 面试题 目录

什么是重绘 (Repaint) 和回流/重排 (Reflow)?什么情况下会触发重绘和回流?如何减少重绘和回流?

推荐答案

重绘 (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)

回流(也称为重排)是指当元素的尺寸、位置、结构等发生改变,导致其在文档流中的位置和大小发生变化,进而影响到其他元素的布局时,浏览器需要重新计算所有受到影响的元素的几何属性(包括位置、大小等),重新构建渲染树的过程。

回流是一个更重的操作,因为它需要重新计算元素的几何属性和布局,而且回流通常会触发重绘。这就像是在画布上重新调整元素的位置和大小,然后再重新着色。

触发重绘和回流的情况详解

触发重绘的情况

  1. 修改元素的颜色属性: 例如:colorbackground-colorborder-color 等。
  2. 修改元素的透明度: 例如:opacity 属性。
  3. 修改阴影和轮廓: 例如:box-shadowoutline 等。
  4. 修改 visibility 属性:visibility 的值在 visiblehidden 之间切换时,也会触发重绘。

触发回流的情况

  1. 页面初次渲染: 当页面加载完成,浏览器需要计算所有元素的布局。
  2. 窗口大小变化: 当用户调整浏览器窗口的大小时,会触发回流以适应新的窗口大小。
  3. 元素的位置和尺寸变化: 包括 widthheightmarginpaddingbordertopleftrightbottom 等属性的改变。
  4. 元素的内容变化: 元素的文本内容改变、图片被替换等会改变元素的尺寸,从而触发回流。
  5. 添加或删除DOM元素: 当有新的 DOM 元素被添加或者有 DOM 元素被删除时,浏览器会重新计算布局。
  6. 激活 CSS 伪类: 例如 :hover 伪类,当鼠标悬停在元素上时可能会触发回流。
  7. 查询某些属性或方法: 比如, offsetTop/Left/Width/Height, clientTop/Left/Width/Height, scrollTop/Left/Width/Height, getComputedStyle(), getBoundingClientRect() 这些操作会触发浏览器立即进行布局计算,因为它们需要元素的最新布局信息。
  8. 改变 font-sizefont-family 等会影响元素大小的样式
  9. 添加 floatposition 属性

如何减少重绘和回流

理解了重绘和回流的原理,我们就可以有针对性地优化性能。

  1. 集中修改样式: 尽量使用 class 来批量修改样式,避免逐条修改元素的 style 属性,从而减少回流次数。例如,可以预先定义好一些样式类,然后一次性添加到元素上,而不是多次修改元素的样式。
  2. 使用 transformopacity 进行动画: transformopacity 属性的改变不会触发回流,只会触发重绘。使用这些属性进行动画可以显著提高动画的性能。例如使用 transform: translate() 来移动元素,而不是修改元素的 topleft
  3. 避免频繁操作DOM: 尽量减少直接操作 DOM 的次数。可以先将 DOM 元素脱离文档流(例如:将元素的 display 属性设置为 none,或者使用 documentFragment),进行批量修改,然后再重新插入到文档流中。
  4. 避免使用 table 布局: table 的布局计算复杂,任何对 table 的修改都可能会导致整个 table 的回流。尽量使用 div 等其他元素进行布局。
  5. 使用 requestAnimationFrame 进行动画: requestAnimationFrame 方法告诉浏览器在下一次重绘之前调用指定的函数。这样可以确保动画在合适的时机进行,从而减少不必要的计算和回流。
  6. 使用 documentFragment 进行批量 DOM 操作: 可以将多个 DOM 操作添加到 documentFragment 中,然后一次性插入到文档中,这样可以减少回流的次数。
  7. 避免强制同步布局: 在修改DOM后立即获取布局信息(例如:offsetTop)。这样会导致浏览器被迫进行回流操作。尽量先批量修改 DOM,然后统一获取布局信息。
  8. 缓存布局信息: 如果需要多次使用某个元素的布局信息(例如: offsetWidth, offsetHeight),可以将这些信息缓存起来,避免重复计算。
  9. 使用 CSS Containment 属性: 可以使用 contain 属性将元素的渲染独立出来,从而减少回流的影响范围。例如使用 contain: layout 可以限制回流只在该元素内发生。

通过以上优化手段,可以有效地减少重绘和回流的发生,从而提高页面的性能和用户体验。

纠错
反馈