问题描述
当我们使用循环时,我发现了一个奇怪的强制回流案例。 Example here
在第一种情况下,我遍历了 200 个元素并使用 for 循环更改了它的类。
function resize1(){
let children = parent1.children;
clicked2 = !clicked2;
for(let i=0;i<200;i++){
let child = children[i];
let { width } = window.getComputedStyle(child);
isLarge = clicked2 ? i % 2 === 0 : i % 2=== 1;
if(isLarge){
child.classList.add('large');
child.classList.remove('small');
}
else{
child.classList.add('small');
child.classList.remove('large');
}
// size = window.getComputedStyle(child).margin;
}
}
我再次遍历每个子元素并更改相同的类,而不是循环。
let width,isLarge,i =0,child;
child = parent.children[i];
width = window.getComputedStyle(child);
isLarge = clicked ? i % 2 === 0 : i % 2=== 1;
if(isLarge){
child.classList.add('large');
child.classList.remove('small');
}
else{
child.classList.add('small');
child.classList.remove('large');
}
i++;
child = parent.children[i];
width = window.getComputedStyle(child);
isLarge = clicked ? i % 2 === 0 : i % 2=== 1;
if(isLarge){
child.classList.add('large');
child.classList.remove('small');
}
else{
child.classList.add('small');
child.classList.remove('large');
}
i++;
(*200 个元素) 我知道这种情况很奇怪,但是当我检查性能时,第二种情况在 chrome 开发工具中花费的时间更少,而且回流也只发生在循环情况下。
解决方法
答案与循环与不循环完全无关,而与您使用 window.getComputedStyle()
的方式有关。
This handy gist 概述了哪些 JavaScript 属性和方法将强制布局/重排。
window.getComputedStyle() 将在以下三个条件之一强制布局:
-
元素在影子树中
-
有媒体查询(与视口相关的查询)。具体而言,以下情况之一:
-
请求的属性是以下之一:
height
,width
现在,在代码的循环版本中,您有:
let { width } = window.getComputedStyle(child);
但是在您的代码的非循环版本中,您有:
width = window.getComputedStyle(child);
有什么区别? width = window.getComputedStyle(child)
创建对计算样式对象的引用,但它不访问 width
属性。您(可能是错误地)创建了一个变量 width
,它不请求宽度属性,而是请求计算的样式对象本身,这本身并不足以强制布局。
但是,let { width } = window.getComputedStyle(child)
添加了将 width
属性解构到变量声明的额外步骤,从而有效地访问该属性并为您的 {{} 的每次 迭代强制布局1}} 循环。
您可以在性能时间线 (Safari) 中看到 for
导致的额外强制布局:
如果您修改代码的非循环版本以访问所有 200 个案例的 let { width } = window.getComputedStyle(child)
的 width
属性,您将在非循环部分获得相同的强制布局:
或者,您可以简单地删除 getComputedStyle()
的所有用途,因为在您的代码的当前版本中它没有用于任何事情,并完全消除强制布局。