浏览器原理
- 异步布局优化
- 刷新也是批处理的,会打标记,然后在下一帧进行统一刷新。
- 强制同步布局
- 打破上述流程,在”写“完之后,紧接着”读“。
- 什么是“读”操作? 就是那些需要获取元素精确几何属性的 API:
- element.offsetWidth, element.offsetHeight
- element.clientWidth, element.clientHeight
- element.scrollWidth, element.scrollHeight
- element.scrollTop, element.scrollLeft
- element.getBoundingClientRect()
- getComputedStyle()
- 什么是“读”操作? 就是那些需要获取元素精确几何属性的 API:
- 打破上述流程,在”写“完之后,紧接着”读“。
- 性能杀手:“布局抖动(Layout Thrashing)”
- “写 -> 读 -> 写 -> 读...” 的交替操作
js
const myElement = document.getElementById('box');
// 1. 写入操作,使布局“失效”,变更被放入队列
myElement.style.width = '200px';
// 2. 读取操作,为了获取准确的 offsetWidth 值...
// ...浏览器不得不强制清空队列,并立即执行布局计算
console.log(myElement.offsetWidth);
错误的示范,频繁读写:
js
function resizeAll() {
const divs = document.querySelectorAll('.box');
const firstBox = divs[0];
// 读取第一个盒子的宽度
const width = firstBox.offsetWidth;
for (let i = 1; i < divs.length; i++) {
// 这是一个灾难性的循环
// 写操作
divs[i].style.width = width + 'px';
// 马上又进行了一个不必要的读操作,只是为了举例
console.log(divs[i].offsetWidth); // 这一行会强制触发一次同步布局
}
}
优化:
js
function resizeAllOptimized() {
const divs = document.querySelectorAll('.box');
const firstBox = divs[0];
// --- 批量读取阶段 ---
const width = firstBox.offsetWidth; // 读一次
// --- 批量写入阶段 ---
for (let i = 1; i < divs.length; i++) {
// 只有写操作,浏览器可以缓存它们
divs[i].style.width = width + 'px';
}
// 在这个函数执行完毕后,浏览器才会进行一次总的布局计算。
// 从 N 次布局优化为了 1 次。
}