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

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 读写分离优化性能

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

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

相关推荐
bin91536 分钟前
前端JavaScript导出excel,并用excel分析数据,使用SheetJS导出excel
前端·javascript·excel
Rattenking14 分钟前
node - npm常用命令和package.json说明
前端·npm·json
Easonmax14 分钟前
【HTML5】html5开篇基础(1)
前端·html·html5
For. tomorrow18 分钟前
Vue3中el-table组件实现分页,多选以及回显
前端·vue.js·elementui
.生产的驴31 分钟前
SpringBoot 消息队列RabbitMQ 消息确认机制确保消息发送成功和失败 生产者确认
java·javascript·spring boot·后端·rabbitmq·负载均衡·java-rabbitmq
布瑞泽的童话1 小时前
无需切换平台?TuneFree如何搜罗所有你爱的音乐
前端·vue.js·后端·开源
白鹭凡1 小时前
react 甘特图之旅
前端·react.js·甘特图
打野赵怀真1 小时前
你有看过vue的nextTick源码吗?
前端·javascript
2401_862886781 小时前
蓝禾,汤臣倍健,三七互娱,得物,顺丰,快手,游卡,oppo,康冠科技,途游游戏,埃科光电25秋招内推
前端·c++·python·算法·游戏