在前端开发中关于reflow(回流)和repaint(重绘)的几点思考

在前端开发中关于reflow(回流)和repaint(重绘)的几点思考

在前端开发中,reflow(回流)和repaint(重绘)是影响网页性能的两个关键概念,下面将对它们进行详细解释,并给出减少其发生次数的方法。

文章目录

什么是 Reflow(回流也叫重排)?定义触发 Reflow 的常见操作

什么是 Repaint(重绘)?定义触发 Repaint 的常见操作

两者关系性能影响如何减少 Reflow 和 Repaint 的次数?批量修改 DOM使用 `requestAnimationFrame` 处理动画避免频繁读取布局信息开启 GPU 加速,使用 `transform` 和 `opacity` 进行动画将元素脱离文档流后进行修改使用position:absolute或position:fixed使元素脱离文档流修改样式使用事件委托处理大量事件优化 CSS 加载顺序

总结

什么是 Reflow(回流也叫重排)?

定义

Reflow 指的是浏览器为了重新计算文档中元素的布局信息(元素的大小、位置、边距等几何信息)而进行的过程。当 DOM 的变化影响了元素的布局信息时,浏览器需要重新计算元素在视口内的位置和尺寸,将其安放到界面中的正确位置,这个过程就被称为 Reflow。

通俗一点讲:无论通过什么方式影响了元素的几何信息(元素在视口内的位置和大小),浏览器都需要重新计算元素在视口内的几何属性,这个过程叫做Reflow(回流或重排)。

触发 Reflow 的常见操作

调整窗口大小(resize)改变字体大小添加或删除 DOM 元素元素的尺寸(宽、高、边距等)发生变化内容变化(文本数量或图片大小改变)计算元素的某些属性(如 offsetWidth、scrollTop 等)

什么是 Repaint(重绘)?

定义

Repaint 是指当一个元素的外观发生改变,但没有影响到布局信息时,浏览器将元素的新外观绘制到屏幕上的过程。Repaint 的开销通常比 Reflow 小,因为它只需处理元素的视觉样式,而不需要重新计算布局。

通俗一点讲:通过构造渲染树和重排(回流)阶段,我们知道了哪些节点是可见的,以及可见节点的样式和具体的几何信息(元素在视口内的位置和尺寸大小),接下来就可以将渲染树的每个节点转换为屏幕上的实际像素,这个阶段就叫做重绘。

触发 Repaint 的常见操作

改变元素的颜色、背景色改变元素的可见性(visibility)改变元素的边框样式改变元素的阴影效果

两者关系

回流一定会导致重绘,因为布局改变后外观必然变化;重绘不一定导致回流,仅样式变化不影响布局时,只需重绘。

性能影响

回流的计算成本远高于重绘,频繁的回流会导致页面卡顿,开发中需要尽量减少(如避免频繁操作DOM样式,使用transform替代位移等).

如何减少 Reflow 和 Repaint 的次数?

为了优化网页性能,应尽量减少 Reflow 和 Repaint 的发生次数,特别是在处理动画、滚动等高频操作时。以下是一些实用的优化方法:

批量修改 DOM

不要频繁地修改 DOM,而是集中修改。例如:

// 低效做法

const el = document.getElementById('myElement');

el.style.width = '100px';

el.style.height = '200px';

el.style.margin = '10px';

// 高效做法:合并样式修改

const el = document.getElementById('myElement');

el.classList.add('new-style'); // 预先定义好 new-style 类

// 或者使用 CSSOM

const el = document.getElementById('myElement');

const style = el.style;

style.cssText += '; width: 100px; height: 200px; margin: 10px;';

使用 requestAnimationFrame 处理动画

对于动画效果,使用 requestAnimationFrame 可以将多次修改集中到一帧中执行,避免不必要的回流:

function animate() {

requestAnimationFrame(() => {

// 在这里进行 DOM 修改

element.style.transform = 'translateX(100px)';

element.style.opacity = '0.5';

});

}

避免频繁读取布局信息

当读取元素的布局信息(如 offsetWidth、scrollTop 等)时,浏览器会强制刷新布局,导致回流。因此,应避免在修改 DOM 的过程中频繁读取这些值:

// 低效做法:读取和修改交替进行

const el = document.getElementById('myElement');

console.log(el.offsetWidth); // 触发回流

el.style.width = '200px'; // 修改样式

console.log(el.offsetWidth); // 再次触发回流

// 高效做法:批量读取,批量修改

const el = document.getElementById('myElement');

const width = el.offsetWidth; // 读取一次

el.style.width = '200px'; // 修改

el.style.height = '300px'; // 修改

// 后续再读取其他布局信息

开启 GPU 加速,使用 transform 和 opacity 进行动画

开启 GPU 加速,利用 css 属性 transform 、will-change 等,比如改变元素位置,我们使用 translate 会比使用绝对定位改变其 left 、top 等来的高效,因为它不会触发重排或重绘,transform 使浏览器为元素创建⼀个 GPU 图层,这使得动画元素在一个独立的层中进行渲染。当元素的内容没有发生改变,就没有必要进行重绘。

transform 和 opacity 不会触发回流和重绘,而是使用 GPU 进行加速,性能更好:

/* 使用 transform 代替 left/top 进行位置移动 */

.element {

transition: transform 0.3s ease;

}

.element:hover {

transform: translateX(50px);

}

将元素脱离文档流后进行修改

对于需要进行大量修改的元素,可以先将其脱离文档流,修改完成后再放回文档中,这样只会触发两次回流(脱离和放回时):

// 1. 隐藏元素

element.style.display = 'none';

// 2. 进行多次修改

element.style.width = '200px';

element.style.height = '300px';

element.style.margin = '10px';

// 3. 重新显示元素

element.style.display = 'block';

使用position:absolute或position:fixed使元素脱离文档流修改样式

默认情况下,HTML元素处于文档流中,它们的位置和大小会影响其他元素。使用position:absolute和position:fixed后,元素会脱离文档流,不再影响其他元素的布局。特别适合频繁动画或定位变动的元素,比如弹窗、浮动框、下拉菜单等。

注意:不能完全避免重排和重绘,初始设置absolute或fixed本身也需要一次重排,修改某些属性(如 width height top left)依然会触发重排和重绘,只是影响范围更小了。

使用事件委托处理大量事件

对于大量元素的事件处理,使用事件委托可以减少 DOM 操作,从而减少回流和重绘:

// 不推荐:为每个元素绑定事件

document.querySelectorAll('.item').forEach(item => {

item.addEventListener('click', handleClick);

});

// 推荐:使用事件委托

document.getElementById('container').addEventListener('click', (e) => {

if (e.target.classList.contains('item')) {

handleClick(e);

}

});

优化 CSS 加载顺序

确保关键 CSS 尽早加载,避免因 CSS 加载延迟导致的回流。可以使用 rel="preload" 预加载重要的 CSS 文件:

总结

Reflow 和 Repaint 是浏览器渲染过程中的必要步骤,但频繁触发会导致性能问题。通过合理规划 DOM 操作、优化 CSS 选择器、使用硬件加速等方法,可以有效减少 Reflow 和 Repaint 的次数,提升网页的响应速度和用户体验。

相关推荐

全球495家科技公司裁员超14万人,企业如何跳出死亡螺旋?
25平方线能带多少千瓦
365bet官网网址是多少

25平方线能带多少千瓦

🗓️ 08-21 👁️ 7243
俠盜獵車手5怎麽買房子(攻略)
365bet365用址

俠盜獵車手5怎麽買房子(攻略)

🗓️ 06-30 👁️ 9693
苹果手机秒表在哪里
365bet官网网址是多少

苹果手机秒表在哪里

🗓️ 10-11 👁️ 5905
共青团中央 全国青联关于印发《青年联合会组织办法》的通知
免费藏文翻译中文
365bet官网网址是多少

免费藏文翻译中文

🗓️ 07-24 👁️ 3868