CSS基础之浏览器的重绘和回流是什么

本文深入探讨了网页布局中的回流与重绘现象,解释了它们对性能的影响,列举了常见的触发因素,并提供了详尽的优化策略,包括CSS和JavaScript的最佳实践。

回流和重回一直以来是我们关注的重点,所以在讨论回流与重绘之前,我们要知道:

  1. 浏览器使用流式布局模型 (Flow Based Layout)。
  2. 浏览器会把HTML解析成DOM,把CSS解析成CSSDOMDOMCSSDOM合并就产生了Render Tree
  3. 有了RenderTree,我们就知道了所有节点的样式,然后计算他们在页面上的大小和位置,最后把节点绘制到页面上。
  4. 由于浏览器使用流式布局,对Render Tree的计算通常只需要遍历一次就可以完成,但table及其内部元素除外,他们可能需要多次计算,通常要花3倍于同等元素的时间,这也是为什么要避免使用table布局的原因之一。

一句话:回流必将引起重绘,重绘不一定会引起回流。

回流:当render tree中部分或者是全部的元素的尺寸、结构或者某些属性发生变化之后,浏览器会重新渲染部分或者全部的文档,就是回流。

导致回流的操作:

  • 页面首次渲染
  • 浏览器窗口大小发生改变
  • 元素尺寸或位置发生改变
  • 元素内容变化(文字数量或图片大小等等)
  • 元素字体大小变化
  • 添加或者删除可见DOM元素
  • 激活CSS伪类(例如::hover
  • 查询某些属性或调用某些方法

一些常用且会导致回流的属性和方法:

  • clientWidthclientHeightclientTopclientLeft
  • offsetWidthoffsetHeightoffsetTopoffsetLeft
  • scrollWidthscrollHeightscrollTopscrollLeft
  • scrollIntoView()scrollIntoViewIfNeeded()
  • getComputedStyle()
  • getBoundingClientRect()
  • scrollTo()

重绘:当页面中的样式改变而并不影响他在文档流中的位置的时候,浏览器会给元素赋予新的样式,然后并且重新绘制文档流,这个过程就是重绘。

回流和重绘对性能的影响:

回流比重绘的代价更大。有的时候,某一个元素的尺寸,位置或者某些属性发生变化了,与之相关的父元素等相关元素都会引起回流。

现代浏览器会对回流和重绘做一些优化:浏览器会维护一个队列,把所有引起回流和重绘的操作放入队列中,如果队列中的任务数量或者时间间隔达到一个阈值的,浏览器就会将队列清空,进行一次批处理,这样可以把多次回流和重绘变成一次。

当你访问以下属性或方法时,浏览器会立刻清空队列:

  • clientWidthclientHeightclientTopclientLeft

  • offsetWidthoffsetHeightoffsetTopoffsetLeft

  • scrollWidthscrollHeightscrollTopscrollLeft

  • widthheight

  • getComputedStyle()

  • getBoundingClientRect()

因为队列中可能会有影响到这些属性或方法返回值的操作,即使你希望获取的信息与队列中操作引发的改变无关,浏览器也会强行清空队列,确保你拿到的值是最精确的。

如何避免回流和重绘制?

CSS

  • 避免使用table布局。
  • 尽可能在DOM树的最末端改变class
  • 避免设置多层内联样式。
  • 将动画效果应用到position属性为absolutefixed的元素上。
  • 避免使用CSS表达式(例如:calc())。

JavaScript

  • 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
  • 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
  • 也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
  • 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
  • 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
### 浏览器渲染机制中的回流 #### 重排(Reflow)与(Repaint)的概念及其区别 在浏览器的渲染过程中,**重排**(也称为回流)是指当 DOM 树或 CSSOM 树发生变化时,浏览器需要新计算元素的位置几何信息,并更新布局的过程。这一过程通常较为昂贵,因为它涉及到大量计算,包括元素的大小、位置以及父子节点之间的关系等[^1]。 ****则是指在不改变元素布局的情况下,仅更新元素的视觉样式,例如颜色、背景色、透明度等。由于不需要新计算布局信息,因此的开销远小于重排[^2]。 两者的主要区别在于: - **重排会触发**:当发生重排时,元素的布局变化会导致其视觉部分也需要制。 - **不会触发重排**:如果只是改变了元素的颜色等外观属性,而没有影响其布局,则只需进行。 #### 触发条件 以下是一些常见的触发重排的操作: - **触发重排**的操作包括但不限于: - 添加或删除可见的 DOM 元素 - 修改元素的尺寸(如宽度、高度) - 修改元素的位置(如 `top`、`left`) - 改变窗口大小 - 获取某些会引起布局刷新的属性,如 `offsetWidth`、`offsetHeight` 等[^1] ```javascript const element = document.getElementById('box'); element.style.width = '200px'; // 修改尺寸,触发重排 console.log(element.offsetWidth); // 获取 offsetWidth,可能触发同步重排 ``` - **触发**的操作通常涉及样式的变化,但不涉及布局调整,例如: - 修改颜色 - 修改背景色 - 修改边框颜色或阴影 - 修改透明度(opacity) ```css .box { background-color: red; transition: background-color 0.3s ease; } ``` ```javascript document.getElementById('box').style.backgroundColor = 'blue'; // 触发 ``` #### 性能优化策略 为了提高页面性能,减少不必要的重排是关键。以下是一些有效的优化手段: - **批量修改 DOM 样式**:避免多次对 DOM 进行单独操作,可以先将多个修改合并后一次性执行,从而减少重排次数[^4]。 ```javascript const element = document.getElementById('box'); element.classList.add('new-style'); // 使用 class 替代 style 操作 ``` - **使用 CSS 动画替代 JS 动画**:对于动画效果,推荐使用 `transform`、`opacity` 等 CSS 属性,这些属性不会引起重排,且支持硬件加速,性能更优[^3]。 ```css .animate { transform: translateX(100px); opacity: 0.5; transition: all 0.3s ease; } ``` - **避免在循环中频繁访问布局属性**:在循环体内读取 `offsetWidth`、`clientHeight` 等属性会强制浏览器同步执行重排,影响性能[^1]。 ```javascript // 不推荐 for (let i = 0; i < elements.length; i++) { console.log(elements[i].offsetWidth); } // 推荐 const widths = []; for (let i = 0; i < elements.length; i++) { widths.push(elements[i].offsetWidth); } console.log(widths); ``` - **复杂动画使用绝对定位**:将动画元素设置为 `position: absolute` 或 `fixed`,使其脱离文档流,避免影响其他元素的布局。 ```css #animation { position: absolute; top: 0; left: 0; } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值