循环导致强制回流,而相同的代码没有循环不会导致回流

Looping causes forced reflow while the same code doesn't cause reflow without loop

当我们使用循环时,我发现了一个奇怪的强制回流案例。 Example here

在第一种情况下,我循环遍历了 200 个元素并使用 for 循环更改其 class。

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;
    }
}

我没有循环,而是再次遍历每个子元素并更改了相同的 class。

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 属性和方法将强制 layout/reflow.

window.getComputedStyle() will force layout in one of 3 conditions:

  1. The element is in a shadow tree

  2. There are media queries (viewport-related ones). Specifically, one of the following:

  3. The property requested is one of the following:

  • height, width

现在,在你的代码的循环版本中,你有这个:

let { width } = window.getComputedStyle(child);

但是在代码的 循环版本中,您有:

width = window.getComputedStyle(child);

有什么区别? width = window.getComputedStyle(child) 创建对计算样式对象的引用,但它 访问 width 属性。您已经(可能错误地)创建了一个变量 width,它 不是 请求宽度 属性 而是计算样式对象本身,它本身不是足以强制布局。

然而,let { width } = window.getComputedStyle(child) 添加了将 width 属性 解构到变量声明的额外步骤,有效地访问 属性 并强制布局 你的for循环的每次迭代。

您可以在性能时间线 (Safari) 中看到 let { width } = window.getComputedStyle(child) 导致的额外强制布局:

如果您修改代码的非循环版本以访问所有 200 种情况下 getComputedStyle()width 属性,您将在非循环中获得相同的强制布局循环部分:

或者,您可以简单地删除对 window.getComputedStyle() 的所有使用,因为在您的代码的当前版本中它没有用于任何用途,并完全消除强制布局。