start
2025年,回首过往,思绪如窗前的苍蝇,嗡嗡作响。此刻,我打开记忆的窗,倒一倒脑子,看看那些岁月里留下了什么。
基本详情
- 不合理的重排与重绘会导致浏览器性能下降,尤其在页面结构复杂或操作频繁时,以下是日常开发中常见的不合理重排与重绘情况汇总:
例1:逐个修改样式属性
- 问题代码:
const ele = document.querySelector('div');
ele.style.width = '100px'; // 触发重排
ele.style.height = '100px'; // 再次触发重排
ele.style.backgroundColor = 'red'; // 触发重绘
- 问题描述:每次修改样式属性时,浏览器都会触发重排或重绘。上述代码中,width 和 height 的修改分别触发了两次重排。
- 优化代码:
const ele = document.querySelector("div");
ele.style.cssText = "width: 100px; height: 100px; background-color: red;";
- 优化描述:从多次修改样式属性,改成一次性修改多个样式属性,减少重排和重绘的次数。
例2:频繁读取布局属性
- 问题代码:
const ele = document.querySelector('div');
ele.style.left = 1 + ele.offsetLeft + 'px'; // 计算元素布局信息,触发重排
ele.style.top = 1 + ele.offsetTop + 'px'; // 再次计算元素布局信息,再次触发重排
- 问题描述:每次读取 offsetLeft 或 offsetTop 时,浏览器都会强制刷新渲染队列以获取最新值,从而触发重排。
- 优化代码:
const ele = document.querySelector('div');
const currentLeft = ele.offsetLeft;
const currentTop = ele.offsetTop;
ele.style.left = 100 + currentLeft + 'px';
ele.style.top = 100 + currentTop + 'px';
- 优化描述:
- 将布局信息缓存到局部变量中,避免重复读取,减少计算元素布局信息,减少重排次数。
- 上面两段代码,优化了两点:
- 相同点:由于都是独立改变元素信息,所以也都触发了两次重排;
- 优化一:第二段代码的优点除了继续使用可以得到优化之外
- 优化二:更重要的是减少了浏览器重复计算布局的次数,从而提升了性能
例3:在循环中频繁修改DOM
- 问题代码:
for (let i = 0; i < 1000; i++) {
const ele = document.createElement('div');
ele.style.width = '10px';
ele.style.height = '10px';
document.body.appendChild(ele); // 每次插入都会触发重排和重绘
}
- 问题描述:在循环中直接向文档中插入元素,会导致浏览器频繁触发重排和重绘。
- 优化代码:
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const ele = document.createElement('div');
ele.style.width = '10px';
ele.style.height = '10px';
fragment.appendChild(ele); // 先将元素添加到文档碎片
}
document.body.appendChild(fragment); // 一次性插入文档
- 优化描述:使用 DocumentFragment 将所有操作离线完成,最后一次性插入文档,减少重排和重绘的次数
例4:使用 transform 替代布局属性
- 问题代码:
const ele = document.querySelector('div');
ele.style.left = '100px'; // 触发重排
ele.style.top = '100px'; // 再次触发重排
- 问题描述:修改 left 和 top 属性会触发重排,因为它们改变了元素的几何位置。
- 运行注意事项:运行代码不会看到变化,需要条件定位属性
position: relative;,这样就可以看到移动现象了 - 优化代码:
const ele = document.querySelector('div');
ele.style.transform = 'translate(100px, 100px)'; // 只触发重绘
- 优化描述:使用 transform 属性代替 left 和 top,因为 transform 不会触发重排,只会触发重绘。
实践过程遇到的新思考
ele.style和ele.style.cssText有什么不同
- 一句话总结:实践中逐步改变
ele.style更优,批量改变ele.style.cssText更优
ele.style
- ele.style 是一个 CSSStyleDeclaration 对象,它提供了直接操作元素样式的接口。通过 ele.style,你可以访问和修改元素的内联样式。
- 特点:
- 直接操作样式属性:可以通过点语法(
.)或方括号语法([])直接访问和修改具体的样式属性。例如:ele.style.width = '100px';ele.style['background-color'] = 'red'; - 动态修改单个样式:适合在运行时动态修改单个样式属性。
- 不覆盖原有样式:修改一个属性不会影响其他内联样式。例如:
ele.style.width = '100px'; // 不会清除其他内联样式
- 直接操作样式属性:可以通过点语法(
- 适用场景:
- 当需要动态修改单个样式属性时。
- 当需要根据条件动态设置样式时。
- 实践体验例子:
<style>
div {
border: solid 1px red;
}
</style>
<button>触发改变</button>
<div>div</div>
<script>
const btn = document.querySelector('button');
const div = document.querySelector('div');
let idx = 0;
btn.addEventListener('click', function () {
idx++;
idx %= 6;
switch (idx) {
case 1:
div.style.width = '100px';
break;
case 2:
div.style.height = '100px';
break;
case 3:
div.style.backgroundColor = 'red';
break;
case 4:
div.style = '';
break;
case 5:
div.style = 'width:200px;height:200px;background:purple;color:#fff;font-size:24px;'
break;
default:
div.style = '';
break;
}
})
</script>
ele.style.cssText
- ele.style.cssText 是一个字符串属性,用于一次性设置或获取元素的所有内联样式。它允许你通过字符串的形式批量设置样式。
- 特点:
- 批量设置样式:通过字符串一次性设置多个样式,避免多次操作 DOM。例如:
ele.style.cssText = 'width: 100px; height: 100px; background-color: red;'; - 覆盖原有样式:设置 cssText 时,会完全覆盖元素的内联样式。如果需要保留原有样式,需要手动拼接字符串。
- 性能优化:在批量设置样式时,比逐个设置 ele.style 更高效,因为浏览器只需要解析一次样式规则。
- 批量设置样式:通过字符串一次性设置多个样式,避免多次操作 DOM。例如:
- 适用场景:
- 当需要批量设置多个样式属性时。
- 当需要优化性能,减少 DOM 操作次数时。
- 实际体验例子:
<style>
div {
border: solid 1px red;
}
</style>
<button>触发改变</button>
<div>div</div>
<script>
const btn = document.querySelector('button');
const div = document.querySelector('div');
let idx = 0;
btn.addEventListener('click', function () {
idx++;
idx %= 4;
switch (idx) {
case 1:
div.style.cssText = 'width:100px';
break;
case 2:
div.style.cssText = 'height:100px';
break;
case 3:
div.style.cssText = 'background-color:red';
break;
default:
div.style.cssText = '';
break;
}
})
</script>
启动硬件加速
transform 属性触发 GPU 加速
- 当使用 transform 时,浏览器会将元素提升到一个独立的渲染层(composite layer),并利用 GPU 进行渲染。
- 特别是当使用 3D 变换(如 translateZ(0) 或 translate3d(0, 0, 0))时,浏览器会明确地将元素放入 GPU 可处理的图层中,从而触发硬件加速。
- 作用:可以显著减少 CPU 的负担,提高动画的流畅性。
div {
transform: translateZ(0); /* 触发 GPU 加速 */
}
will-change 属性触发 GPU 加速
- will-change 属性用于提前告知浏览器某个元素即将发生变化,从而让浏览器提前进行优化。
- 当设置 will-change 属性时,浏览器会为该元素创建一个独立的图层,并可能将其渲染任务交给 GPU。
- 作用:可以让浏览器提前为动画或变化做好准备,减少渲染开销,提高性能。
div {
will-change: transform; /* 提前告知浏览器元素将要变化 */
}
- 注意事项:
- 适度使用:过度使用 GPU 加速可能会导致内存占用过高,尤其是在移动端设备上。
- 性能优化:虽然 transform 和 will-change 可以提升性能,但应避免滥用,以确保页面的整体性能。
网页使用GPU的场景
- 图形渲染:3D 渲染、2D 图形加速
- 动画效果:CSS 动画加速、JavaScript 动画
- 视频处理:视频解码与播放、实时视频处理
- GPU技术:WebGL 和 WebGPU
- 计算密集型任务:Web Workers 和 GPU 计算、机器学习和 AI
- 数据可视化:大规模数据可视化、实时数据更新
- 网页游戏:2D 和 3D 游戏、多人在线游戏
总结
- 优化的基本目标就是:
- 减少重排重绘的触发次数,从逐个操作优化成批量操作
- 避免强制刷新渲染队列,避免在布局操作中读取布局属性,利用好变量
- 使用硬件加速,使用transform或will-change属性触发GPU加速。
end
恳请诸位同仁、挚友、兄弟姐妹不吝赐教,多多指点!