最近有同学问我怎么理解回流和重绘?什么场景下会触发?我觉得这个非常有意思,值得聊一聊,算是每个前端必须掌握的知识。

首先,回流(Reflow)和重绘(Repaint)是浏览器渲染页面时两个非常重要的过程。它们之间有些联系,但也有很大的区别。简单来说,回流和重绘是两种不同类型的浏览器计算,分别负责页面布局的重新计算和样式的重新渲染。

1. 回流(Reflow)

回流是浏览器对页面布局进行重新计算的过程。在这个过程中,浏览器会计算出元素的位置、尺寸等几何信息。每当页面的布局发生变化时,都会触发回流。

触发回流的场景:

  • 添加或删除 DOM 元素:当新增或删除可见的 DOM 元素时,浏览器必须重新计算页面布局。
  • 元素位置变化:当元素的 position(如 lefttop)发生变化,浏览器需要重新计算其相对于其他元素的位置。
  • 元素尺寸变化:例如,修改了元素的宽度、高度、边距、边框、内边距等都会导致回流。
  • 内容变化:文本内容变化或图片替换成不同尺寸的图片时,浏览器需要重新计算布局。
  • 窗口大小变化:浏览器窗口的大小变化会影响元素的布局,因此会触发回流。
  • 获取布局信息:一些属性如 offsetWidthoffsetHeightclientWidth 等,都会导致浏览器立即进行回流计算。

回流通常是比较昂贵的操作,因为它需要重新计算页面上多个元素的位置和尺寸,可能会影响到页面的渲染性能。

2. 重绘(Repaint)

重绘是在回流之后发生的操作,它是浏览器在页面布局计算完成后,重新绘制元素的外观(颜色、背景、边框等)。重绘并不涉及重新计算布局或位置,通常仅仅是更新元素的视觉效果。

触发重绘的场景:

  • 颜色变化:例如,修改元素的 colorbackground-color,这种操作不会影响布局,只会改变元素的外观,因此会触发重绘。
  • 文本方向变化:例如,修改元素的 text-aligndirection
  • 阴影修改:如 box-shadowtext-shadow 的变化。

注意: 每次回流都会伴随重绘,但不是所有重绘都会引发回流。

3. 如何减少回流和重绘?

回流和重绘会影响页面的渲染性能,因此我们通常会尽量减少它们的触发频率。以下是一些优化建议:

减少回流:

  • 避免频繁修改 DOM:尽量将 DOM 操作合并,例如批量插入节点时,可以使用 DocumentFragment
  • 避免修改尺寸和位置的属性:尽量避免频繁修改 widthheighttopleft 等属性。
  • 尽量使用 class 来改变样式:比起直接通过 style 属性修改单个元素的样式,使用 class 来批量修改样式,能减少回流的次数。
  • 避免使用 table 布局table 布局中的元素修改会导致整个表格的回流。
  • 脱离文档流:对于动画元素,使用 position: fixedabsolute,可以让它们脱离文档流,从而减少对页面其他元素的影响。
  • 硬件加速:使用 CSS3 动画时,利用硬件加速(如使用 transformopacity)可以避免触发回流。

减少重绘:

  • 减少样式修改:仅在需要的时候修改样式,避免不必要的 colorbackground-color 等样式修改。
  • 使用 CSS 动画:尽量使用 CSS 动画代替 JavaScript 动画,这样可以让浏览器利用 GPU 加速,减少对性能的影响。

浏览器的优化机制

现代浏览器会采用优化策略来减少回流和重绘的影响。例如,浏览器会将多个 DOM 操作放入一个队列中批量执行,直到满足一定条件(如操作数量达到阈值或者一定的时间间隔),再一起刷新页面。这种机制有助于提高性能,避免频繁的重排和重绘。

但是,如果你获取一些布局信息(如 offsetHeight 等),浏览器就会强制刷新这些队列,从而触发回流和重绘。所以在获取这些属性时,需要注意性能消耗。