【浏览器】回流与重绘
回流是指在渲染页面时,由于DOM结构或尺寸的改变,需要重新计算元素的几何属性和页面布局。当发生回流时,浏览器需要重新计算每个受影响元素的位置和尺寸,然后重新绘制整个页面。回流(Reflow)和重绘(Repaint)是浏览器渲染过程中的两个关键概念,它们与修改DOM和CSS相关。虽然这两个过程都是渲染的一部分,但它们的开销和影响是不同的。通过合理优化和设计,可以减少回流和重绘的次数,提高网页的响应速
页面的绘制
- 解析 html ⽣成 DOM 树
- 处理 css ⽣成 cssom 树
- 将 dom 树和 cssom 树合并⽣成 render Tree(渲染树:不包括 display:none,head 节点,但是包括 visibility:hidden 的节点)
- 根据 render Tree, 得到每个节点的⼏何信息(位置与⼤⼩:margin,padding,width,height 等)
- 将上⾯的到的⼏何信息传送到 GPU进⾏绘制。
概述
回流(Reflow):
回流是指在渲染页面时,由于DOM结构或尺寸的改变,需要重新计算元素的几何属性和页面布局。这个过程涉及了更多的计算工作,因此比重绘开销更大。当发生回流时,浏览器需要重新计算每个受影响元素的位置和尺寸,然后重新绘制整个页面。
导致回流的操作包括:
- 添加、删除、修改DOM元素(例如改变文本内容)
- 改变元素的位置、尺寸、边距、边框等属性
- 浏览器窗口大小改变
- 激活CSS伪类(如:hover)
- 添加或删除样式表
回流不仅影响单个元素,它可能会影响到整个渲染树,进而触发连锁回流。因此,需要尽量避免频繁的DOM和样式改变,以减少回流的次数。
重绘(Repaint):
重绘是指在元素的外观改变但几何属性没有改变时,浏览器会重新绘制元素。这个过程开销相对较小,因为不涉及布局的重新计算。浏览器只需要根据新的样式重新绘制元素,而无需重新定位和计算大小。
导致重绘的操作包括:
- 修改元素的颜色、背景、边框等属性,但不影响其尺寸和位置
- 激活CSS伪类(如:hover)
⚡ 如何减少 重排和重绘
✅ 样式操作优化
-
合并多次修改
// ❌ 多次触发重排 el.style.width = "100px"; el.style.height = "100px"; el.style.margin = "10px"; // ✅ 一次性修改 el.style.cssText = "width:100px; height:100px; margin:10px;";
或者使用
classList.add()
批量切换 class。 -
避免频繁读取导致强制回流的属性
比如offsetHeight
、getBoundingClientRect
,要么缓存,要么用requestAnimationFrame
节流。浏览器为了性能优化,会把 DOM 操作(增删改样式、布局计算)放进一个队列里,等到下一帧(16ms 左右)统一处理。
但是,当你调用 读取布局信息的 API 时,比如:
- getComputedStyle(element)
- element.getBoundingClientRect()
- element.offsetWidth / offsetHeight
- element.scrollTop / scrollLeft
浏览器必须 返回一个“最新、准确”的值。
这就迫使它把队列里的修改先执行掉,再返回结果。这个过程就叫 强制同步布局 (Forced Synchronous Layout)。
👉 如果 DOM 修改确实影响了布局,那么这一步就会触发 回流。
✅ DOM 操作优化
-
离线操作 DOM,再一次性插入
- 使用
DocumentFragment
- 使用
cloneNode
→ 修改 → 替换原节点
const frag = document.createDocumentFragment(); for (let i = 0; i < 1000; i++) { const li = document.createElement("li"); li.textContent = i; frag.appendChild(li); } ul.appendChild(frag);
- 使用
-
批量修改时,先
display:none
→ 修改 → 再显示
隐藏时修改不会触发回流,最后只触发一次。
✅ CSS 和渲染层优化
-
避免使用影响范围大的 CSS
- 比如
table
布局修改会引发整个表格回流 - 少用
float
,尽量使用flex
或grid
- 比如
-
使用
transform
和opacity
来代替会触发回流的属性left/top/width/height
→ 会回流transform: translate/scale/rotate
、opacity
→ 只触发合成层变化,不回流
/* ✅ GPU 加速,避免回流重绘 */ transform: translateX(100px); opacity: 0.5;
-
开启独立合成层(让元素在单独的渲染层上操作)
will-change: transform;
transform: translateZ(0);
position: fixed/absolute;
(部分场景,脱离文档流)
✅ JS 和渲染协调
-
读写分离
// ❌ 交替读写:多次强制回流 el.style.width = "100px"; console.log(el.offsetHeight); el.style.height = "100px"; // ✅ 先读后写 const h = el.offsetHeight; el.style.width = "100px"; el.style.height = "100px";
-
节流 / 防抖
对resize
、scroll
、input
等高频事件使用节流/防抖,减少触发频率。 -
使用虚拟滚动 / 分页渲染
- 渲染超大列表时,不要一次性插入上千 DOM,使用虚拟列表技术或分页。
总结: “批量操作、读写分离、用 transform/opacity、减少层级影响、用离线 DOM、节流高频操作。”
更多推荐
所有评论(0)