透过performance探究js操作dom样式时浏览器会做什么?
目录
浏览器对回流(重排)、重绘的优化策略
“优化队列”
如果只是读取“敏感”属性,没有对节点进行几何改变,js执行期间仅会进行样式计算,不会强制布局。
数据缓存
改变节点的几何位置,会导致缓存失效
测试环境
Chrome 版本 96.0.4664.45(正式版本) (64 位)
测试代码
点击查看代码
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<div id="div">
<div></div>
</div>
<button>点击测试</button>
<script>
const div = document.getElementById('div');
const btn = document.querySelector('button');
btn.onclick = () => {
// do something
};
</script>
设置节点的style时浏览器的多种处理情况
仅观察代码中btn
点击事件附近相关的浏览器处理
浏览器在js执行期间什么都不做
js执行之后进行样式计算、更新分层树、绘制、合成
测试情况
点击事件中没有代码
btn.onclick = () => {
// do something
};
设置无关元素几何(位置、尺寸等)的属性
btn.onclick = () => {
div.style.color = 'red';
};
设置无关元素几何(位置、尺寸等)的属性并读取该元素的一些样式属性
btn.onclick = () => {
div.style.color = 'red';
const { color, width, height } = div.style;
}
performance记录
js执行之后进行样式计算、布局、更新分层树、绘制、合成
比上一个多了一个布局步骤
测试情况
设置有关元素几何(位置、尺寸等)的属性
btn.onclick = () => {
div.style.height = '100px';
}
设置有关元素几何(位置、尺寸等)的属性并读取该元素的非“敏感”样式属性
btn.onclick = () => {
div.style.height = '100px';
const { color, width, height } = div.style;
}
performance记录
浏览器在js执行期间进行样式计算
样式计算后,如果有新的样式设置,会将其放到“渲染优化队列”中
读取“敏感”属性
btn.onclick = () => {
const top = div.scrollTop,
width = div.clientWidth;
};
performance记录
读取“敏感”属性后并设置无关节点几何位置的样式
btn.onclick = () => {
const top = div.scrollTop;
div.style.color = 'red';
div.style.backgroundColor = 'blue';
};
performance记录
读取“敏感”属性后并设置有关节点几何位置的样式
btn.onclick = () => {
const top = div.scrollTop;
div.style.color = 'red';
div.style.height = '100px';
};
performance记录
读取“敏感”属性后并设置无关节点几何位置的样式,之后再次读取“敏感”属性
浏览器会执行两次样式计算
btn.onclick = () => {
const clientHeight = div.clientHeight;
div.style.color = 'red';
const clientWidth = div.clientWidth;
};
performane记录
读取“敏感”属性后并设置有关节点几何位置的样式,之后再次读取“敏感”属性
浏览器会在js执行期间进行两次样式计算并强制同步布局。
btn.onclick = () => {
const clientHeight = div.clientHeight;
div.style.height = '100px';
const clientWidth = div.clientWidth;
};
performane记录
与节点的class相关的浏览器处理操作
浏览器在js执行期间什么也不做。所以这就是网上说的使用class
样式减少对节点style
的操作?
.style {
color: red;
height: 100px;
}
btn.onclick = () => {
const className = div.className;
div.className = `style`;
// 或者下面的代码
/*
div.classList.add('style');
const className = div.classList.toString();
*/
};
performance记录
关于window.getComputedStyle()
优化回流、重绘
- 网上说的推荐使用class类名/cssText设置样式少用style操作dom可以减少浏览器的一些操作(样式计算、强制同步布局等),减少性能消耗
- 网上说的操作style时,读写分离,先读后写,也可以减少一些样式计算的性能消耗
- "离线"修改dom
- 使用
documentFragment
- 克隆需要操作的节点,操作dom完成后,再替换被克隆的节点
- 使用
display: none
隐藏节点,操作dom完成后,再显示节点。最多有两次回流,避免了可能的更多的回流。
- 使用
- 使用变量缓存“敏感”属性(除了getComputedStyle,currentStyle),
offsetWidth
等的值是数字,没有引用关系,可以被当作一个新值使用 - 使用绝对定位,减少其对其他节点的影响
- 没事儿不要用window.getComputedStyle()获取样式
来源
https://www.phpied.com/rendering-repaint-reflowrelayout-restyle/
https://dl.acm.org/doi/10.1145/1772690.1772741
https://yonatankra.com/layout-reflow/
https://developer.mozilla.org/zh-CN/docs/Web/API/Window/getComputedStyle