浅析浏览器的回流与重绘(Reflow & Repaint)

一、引言

在前端开发中,理解回流和重绘对性能优化来说比较重要。回流和重绘是浏览器渲染页面时的两个重要过程。本文将介绍回流和重绘的概念,并提供一些优化技巧来减少其性能开销。

二、页面渲染

浏览器获取到页面资源后做了什么?

1.解析HTML:浏览器会将接收到的HTML代码解析成DOM树,构建出页面的结构。

2.解析CSS:浏览器会解析CSS代码,构建出CSSOM,表示页面的样式信息。

3.构建渲染树:浏览器会将DOM树和CSSOM合并,生成渲染树(Render Tree),渲染树只包含需要显示的元素和对应的样式信息。

4.布局计算:浏览器会根据渲染树的信息,计算出每个元素在页面中的位置和大小,这个过程称为布局计算(也叫回流)。

5.绘制:浏览器会遍历渲染树,根据每个元素的位置、大小和样式信息,将页面内容绘制到屏幕上。

6.栅格化:如果页面中有一些复杂的元素,比如使用了CSS3的变换或者动画,浏览器会将这些元素单独处理,生成位图,以提高性能。

7.合成和显示:浏览器会将绘制的内容进行合成,并将最终的图像显示在屏幕上。

浏览器使用流式布局模型 (Flow Based Layout)。

浅析浏览器的回流与重绘(Reflow & Repaint)
(图片来自网络)

三、回流和重绘是什么?

回流,也叫重排,是指当 DOM 元素的几何属性(例如位置、大小)发生改变时,浏览器重新计算并更新元素的几何属性,并重新构建页面布局树的过程。回流会导致其他元素的几何属性和布局发生变化。回流是一种相对较慢的操作,会消耗大量的CPU资源。

重绘是指当 DOM 元素的样式属性(例如颜色、背景)发生改变时,浏览器重新绘制元素的过程,但并不影响元素的几何属性和布局。相比于回流,重绘的性能开销较小。

回流一定会导致重绘,但是重绘不一定会导致回流。回流相对较慢,会重新计算文档中元素的位置和几何属性。而重绘是根据元素的新样式重新绘制页面,不需要重新计算元素的位置和几何属性。

四、回流和重绘的性能影响

回流和重绘是比较昂贵的操作,它们会消耗大量的计算资源和时间。频繁的回流和重绘会导致页面性能下降,造成卡顿和延迟响应。

五、触发回流和重绘的情况

1. 触发重绘的情况

  1. 颜色属性:color、background-color、border-color等。
  2. 文字属性:font-weight、font-style、text-decoration等。
  3. 文本属性:text-align、text-transform、line-height等。
  4. 背景属性:background-image、background-position、background-size等。
  5. 盒子模型属性:box-shadow、outline-color、outline-style等。
  6. 渐变属性:linear-gradient、radial-gradient等。
  7. 变形属性:transform、transform-origin等。
  8. 过渡属性:transition、transition-property、transition-duration等。

上述操作只会触发重绘而不会引起回流。因为这些属性的修改只会影响元素的外观,而不会影响元素的布局。

2. 触发回流的情况

  • 盒模型属性:width、height、padding、margin、border等。
  • 定位属性:position、top、left、bottom、right等。
  • 布局属性:display、float、clear、flex等。
  • 字体属性:font-size、line-height、text-align等。
  • 背景属性:background、background-color、background-image等。
  • 盒子模型属性:box-sizing、border-box、outline等。
  • 可见性属性:visibility、opacity等。
  • 修改浏览器窗口大小。
  • 页面初始加载。
  • 页面的渲染树发生改变,如添加或删除元素等。
  • 获取元素的一些布局属性,如offsetWidth、offsetHeight、clientWidth、clientHeight、getComputedStyle()、scrollIntoView()、scrollTo()、getBoundingClientRect()、scrollIntoViewIfNeeded()等。

当进行回流操作时,不仅会影响当前元素,还会影响该元素的子元素以及其他相关联的元素,因为它们的位置和尺寸也会受到影响而需要重新计算。

六、如何优化回流和重绘?

1. 使用CSS3动画

使用 CSS3 动画代替JavaScript动画(例如使用 transform 和 opacity 属性),可以利用 GPU 加速,减少回流和重绘的开销,性能更好。

2. 避免频繁访问布局信息

避免在 JavaScript 中频繁访问元素的布局信息(例如 offsetTop、offsetLeft、clientWidth、clientHeight 等),因为这会强制浏览器进行回流操作。

3. 批量修改样式

对需要修改样式的元素进行批量操作,将多个样式的修改合并为一次操作,避免频繁操作样式,减少回流和重绘的次数。

4. 使用transform属性

使用 transform 属性进行位移、缩放和旋转操作,因为它不会引起回流,只会触发重绘。

5. 使用文档片段

使用文档片段(DocumentFragment)进行DOM操作:将DOM操作放在文档片段中,最后一次性插入文档,减少回流次数。

6. 使用虚拟DOM

通过虚拟DOM的比较算法,最小化真实DOM的变动,减少回流和重绘的次数。

7. 其他

避免使用table布局,在DOM树的最末端改变class,避免设置多层内联样式。避免使用CSS表达式(例如:calc())。使元素脱离文档流。避免频繁读取会引发回流/重绘的属性等等。

8. 代码示例

// 优化回流和重绘的实践
const element = document.getElementById("myElement");

// 使用文档片段进行DOM操作
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
  const div = document.createElement("div");
  div.textContent = "Element " + i;
  fragment.appendChild(div);
}
element.appendChild(fragment);

// 使用虚拟DOM进行DOM操作
const virtualDOM = [
  { id: 1, text: "Element 1" },
  { id: 2, text: "Element 2" },
  // ...
];
function render() {
  const container = document.getElementById("myContainer");
  container.innerHTML = "";
  virtualDOM.forEach((item) => {
    const div = document.createElement("div");
    div.textContent = item.text;
    container.appendChild(div);
  });
}
render();

// 使用CSS动画代替JavaScript动画
element.classList.add("animated");

减少回流和重绘的次数可以提升页面性能和用户体验。

七、最后的话

在JavaScript中,回流(Reflow)和重绘(Repaint)是两个性能优化方面非常重要的概念。了解这两个概念的含义和如何优化可以帮助我们编写更高效的代码,提升网页的性能。

能力一般,水平有限,本文可能存在纰漏或错误,如有问题欢迎大佬指正,感谢你阅读这篇文章,如果你觉得写得还行的话,不要忘记点赞、评论、收藏哦!祝生活愉快!

原文链接:https://juejin.cn/post/7248207744087769125 作者:牛哥说我不优雅

(1)
上一篇 2023年6月25日 上午10:56
下一篇 2023年6月25日 上午11:09

相关推荐

发表回复

登录后才能评论