深度解析浏览器重排和重绘:优化前端性能的终极指南

1. 了解浏览器的重排和重绘

1.1 什么是浏览器的重排和重绘

在网页开发中,浏览器的渲染过程涉及两个重要概念:重排(Reflow)重绘(Redraw)

  • 重绘(Redraw) :当某些元素的外观改变,但不影响它在文档流中的位置时,触发重绘。这意味着浏览器会重新绘制受影响元素的外观,但不会改变它们的布局。
  • 重排(Reflow) :当页面布局发生变化,影响了元素的位置和尺寸时,触发重排。这会导致整个渲染树的重新计算,涉及布局和几何属性的更新。

1.2 重排和重绘的关系

1.2.1 重排不一定导致重绘,但重绘一定会导致重排。

这意味着当我们只改变了元素的颜色、背景等外观属性时,只会触发重绘而不会引起整个页面布局的改变。但一旦改变了影响元素位置和尺寸的属性,将触发重排,进而导致重绘。

1.2.2 代价高昂的性能开销,需谨慎处理。

重排和重绘都会消耗大量的计算资源,特别是在频繁触发的情况下,会影响页面的性能和用户体验。因此,在前端开发中,需要谨慎处理操作,以减少不必要的重排和重绘。

2. 操作导致的重排和重绘

2.1 重绘的常见操作

某些操作会触发浏览器的重绘,即元素的外观发生变化,但不影响它们的布局。以下是常见导致重绘的操作:

  • 修改颜色:修改元素的颜色属性(color、background、border-color等)会触发重绘。
  • 调整边框样式:改变边框的样式(border-style)会导致重绘。
  • 调整阴影效果:修改元素的阴影效果(box-shadow)会触发重绘。
  • 文本修饰:对文本进行修饰,如修改文本装饰属性(text-decoration),会导致重绘。

2.2 重排的常见操作

重排涉及到页面布局的改变,通常由以下操作引起:

  • 添加或删除可见的DOM元素。
  • 改变元素的位置:修改元素的位置,例如通过改变定位属性(position)或调整浮动(float)。
  • 改变元素的尺寸:包括边距、填充、边框、宽度和高度等属性的修改。
  • 修改元素的内容:例如改变文字数量、图片大小等。
  • 改变元素的字体大小。
  • 改变浏览器窗口的尺寸,例如发生resize事件。
  • 激活CSS伪类,如:hover。
  • 查询某些属性或调用计算方法,如offsetWidth、offsetHeight等,以及getComputedStyle等。

2.3 重排影响的范围

2.3.1 全局范围(全局布局): 从根节点html开始对整个渲染树进行重新布局。

2.3.2 局部范围(局部布局): 对渲染树的某部分或某一个渲染对象进行重新布局。

3. 影响性能的重排和重绘操作

3.1 全局范围的重排

3.1.1 页面初始渲染

页面的初始渲染是开销最大的一次重排,涉及整个渲染树的构建和布局。

3.1.2 改变根节点 HTML

修改根节点HTML的任何部分都会触发全局重排,因为它影响了整个渲染树的结构和布局。

3.2 局部范围的重排

3.2.1 针对渲染对象的改变

对渲染对象进行操作可能导致局部范围的重排,这涉及到重新布局渲染树的一部分。这包括但不限于:

  • 添加/删除可见的DOM元素
  • 改变元素的位置
  • 修改元素的尺寸,如边距、填充、边框、宽度和高度等
  • 改变元素的内容,如文字数量、图片大小等

4. 重排的性能优化建议

4.1 样式集中改变

4.1.1 合并样式修改

在操作DOM时,建议集中进行样式修改,而不是逐个添加或修改属性值。这样可以减少重排次数。

xml 复制代码
<!-- 示例HTML -->
<span id="demo">
  我是demo
</span>
ini 复制代码
// 示例JavaScript
const renderEle = document.getElementById('demo');
renderEle.style.color = 'red'; // 导致重绘
renderEle.style.background= '#ccc'; // 导致重绘
renderEle.style.padding = '15px 20px'; // 导致重排(重排会引起重绘)

可以改为:

javascript 复制代码
// 优化后的JavaScript
document.getElementById('demo').className = 'demo'; // 添加class 统一添加/修改样式
css 复制代码
/* 示例CSS */
.demo {
  color: red;
  background: #ccc;
  padding: 15px 20px;
}
4.1.2 使用动态添加 class

动态添加class可以合并样式的修改,从而减少重绘次数。

4.2 将 DOM 离线

4.2.1 脱离文档流操作

在对DOM节点有较大改动时,可以先将元素脱离文档流,进行操作,然后再将其放回文档流。这可以减少重排次数。

xml 复制代码
<!-- 示例HTML -->
<span id="demo">
  我是demo
</span>
ini 复制代码
// 示例JavaScript
const renderEle = document.getElementById('demo');
renderEle.style.display = 'none'; // 导致重排(重排会引起重绘)
renderEle.style.color = 'red'; // DOM不存在渲染树上不会引起重排、重绘
renderEle.style.background= '#ccc';// DOM不存在渲染树上不会引起重排、重绘
renderEle.style.padding = '15px 20px';// DOM不存在渲染树上不会引起重排、重绘
renderEle.style.display = 'block';// 导致重排(重排会引起重绘)
4.2.2 使用定位脱离文档流

将需要重排的元素,position属性设为absolute或fixed,减小重排范围。

xml 复制代码
<!-- 示例HTML -->
<div id='demo'>
  <span id="demo-one">
    我是demo 1号
  </span>
  <span id="demo-two">
    我是demo 2号
  </span>
  <span id="demo-there">
    我是demo 3号
  </span>
</div>
ini 复制代码
// 示例JavaScript
const renderEle = document.getElementById('demo-one');
renderEle.style.position = 'fixed'; // 导致重排(重排会引起重绘)
renderEle.style.padding = '15px 20px'; // 导致重排(只有当前元素)
renderEle.style.height = '60px'; // 导致重排(只有当前元素)

5. 读写分离的优化策略

5.1 缓存写入的值

5.1.1 在写入值时进行缓存

在写入值的过程中,可以通过缓存的方式避免直接读取导致的重排,从而提高性能。

xml 复制代码
<!-- 示例HTML -->
<span id="demo">
  我是demo
</span>
ini 复制代码
// 示例JavaScript
const offsetWidth = '100px';
const renderEle = document.getElementById('demo');
renderEle.style.offsetWidth = offsetWidth // 导致重绘(写入)
const tempoOffsetWidth = renderEle.style.offsetWidth // 读取可能会导致重排

可以优化为:

ini 复制代码
// 优化后的JavaScript
const offsetWidth = '100px';
const renderEle = document.getElementById('demo');
renderEle.style.offsetWidth = offsetWidth // 导致重绘(写入)
const tempoOffsetWidth = renderEle; // 避免直接读取offsetWidth
5.1.2 读写分离优化性能

通过读写分离的方式,将写入和读取操作分开,避免直接读取导致的性能损耗。

这种优化策略适用于一些浏览器在获取属性时可能触发重排的情况,通过缓存写入的值,可以避免不必要的性能开销。

相关推荐
桂月二二4 小时前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
CodeClimb5 小时前
【华为OD-E卷 - 第k个排列 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
hunter2062065 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb5 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角5 小时前
CSS 颜色
前端·css
九酒6 小时前
从UI稿到代码优化,看Trae AI 编辑器如何帮助开发者提效
前端·trae
浪浪山小白兔7 小时前
HTML5 新表单属性详解
前端·html·html5
lee5767 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm
2401_897579657 小时前
AI赋能Flutter开发:ScriptEcho助你高效构建跨端应用
前端·人工智能·flutter
光头程序员7 小时前
grid 布局react组件可以循数据自定义渲染某个数据 ,或插入某些数据在某个索引下
javascript·react.js·ecmascript