【问题标题】:Fastest way to dynamically update the style of millions of DOM elements动态更新数百万个 DOM 元素样式的最快方法
【发布时间】:2013-11-15 22:14:28
【问题描述】:

我仅使用 DOM 在浏览器中构建了 Conway 的生命游戏。没什么特别的,以前做过。我的目标是尽我所能优化它。我实际的生命游戏代码运行良好,而且速度快到我喜欢的程度。瓶颈出现在更新屏幕状态。屏幕上有数十万或数百万个 DOM 元素,您可以想象这可能会很慢(尽管比我最初想象的要快)。我的问题是:

一次在屏幕上处理超过一百万个 DOM 元素,遍历 DOM 元素列表并单独更改其样式的最快方法是什么?

我正在使用一个类来跟踪样式,动态更改它们的style 而不是class 会更好吗?我将所有这些元素保存在一个多维数组中,通过另一种方式迭代会更好(循环本身不是瓶颈,我的代码中有很多这样的循环对我来说运行得足够快)?我对“重排”或元素更改时浏览器如何呈现事物一无所知。能否以提高性能的方式利用这些想法?

这是我当前的代码:

var updateUI = function () {
    for (var i = 0; i < width; i++) {
        var row = grid[i];
        var rowUI = gridUI[i];
        for (var j = 0; j < height; j++) {
            rowUI[j].className = "b" + row[j];
        }
    }
}

类风格为:

.b1 {
    background: #000;
}

http://jsfiddle.net/WE5jQ/

【问题讨论】:

  • DOM 不擅长这种事情。我尖刻的回答是:不要,改用画布。
  • 我明白这一点,并且知道这一点进入了这个项目:)我希望至少有一个“使用画布”评论。
  • 在 jsperf.com 中设置比较测试来评估
  • 干得好@MattDiamant!

标签: javascript jquery html css performance


【解决方案1】:

不要更新每一个的样式。尽可能使用最通用的选择器,并在样式表中操作实际的样式规则。让浏览器完成剩下的工作。

【讨论】:

  • 公平地说,浏览器无论如何都在做这项工作......但你的观点是有效的。
  • 您能解释一下如何使用我给出的代码来实现这一点吗?在我的示例中,我似乎无法做到这一点。每个元素的样式都需要独立于所有其他元素。没有任何情况下我可以一次操作多个。
【解决方案2】:

您可以将网格划分为更小的区域,然后仅将样式更新到被认为是“脏”的区域中的元素(与之前的迭代相比,至少包含一个更改的元素)。这应该会显着减少您在每次更新 UI 调用中需要执行的操作量,尤其是当我们讨论数百万个元素时。

【讨论】:

  • 这是个好主意,我已经计划实施这个版本。保留需要更新的“更改列表”。
【解决方案3】:

如果你想优化你的循环性能,你可以使用Duff's Device,因为你有大量的迭代。这是一种展开循环体的技术,这样每次迭代实际上都完成了多次迭代的工作。 Jeff Greenberg 被认为是第一个将 Duff 的设备发布到 JavaScript 的端口。算法如下:

//credit: Jeff Greenberg
var i = items.length % 8;

while(i){
    process(items[i--]);
}

i = Math.floor(items.length / 8);

while(i){
    process(items[i--]);
    process(items[i--]);
    process(items[i--]);
    process(items[i--]);
    process(items[i--]);
    process(items[i--]);
    process(items[i--]);
    process(items[i--]);
}

在 500,000 次迭代时,执行时间比常规循环少 70%。

【讨论】:

  • 我希望看到一个衡量优势的 jsperf。也许 5 或 10 年前的 JavaScript 引擎从中受益,但现在我不确定他们是否这样做。
  • 这是优化循环的好方法,我曾想过稍微展开循环,但正如我所提到的,我循环的方式并没有造成瓶颈。大部分工作是我应用风格的方式,这是我试图优化的。不过,我肯定会看到这样做会如何影响性能。
  • 这是一个 JSPerf,它将常规循环与 Duff 设备的多个版本进行比较。 jsperf.com/duffs-device/28 在 500,000 次迭代中,当我在 Chrome 31 中运行常规循环时,它比任何“快速”Duff 的设备测试用例慢 70%。
  • 是的,但在循环体中几乎没有做任何事情。在真正的 javascript 代码中,几乎不会出现循环体如此微不足道的情况,以至于手动进行这种优化是有意义的。在 OP 的情况下,循环体(字符串连接,DOM)中正在完成大量工作。
  • 真的吗?否决票? OP 询问“我将所有这些元素保存在一个多维数组中,通过另一种方式进行迭代会更好吗”这可能无法解决所有性能瓶颈,但这显然是针对该特定问题的更高效的解决方案。在 500,000 - 1,000,000+ 次迭代中,您应该始终采用这样的技术,而不管循环中正在完成的其他工作,我不敢相信您会尝试以其他方式争论。
【解决方案4】:

由于这个问题被标记为 jQuery,您可以使用它来添加/删除您想要的类。首先,获取您在元素中使用的通用 id/类,然后切换您想要的类:

$('.generic-class').toggleClass('.another-class');

这样,您将向所有具有.generic-class 的现有元素添加.another-class

在你的 CSS 中:

.another-class { //other styles that you want to apply. }

【讨论】:

  • 使用 jQuery 很慢。我的代码最初使用$('#g' + i + j)[0].className = "b" + row[j]; 来获取单元格,但我通过缓存 dom 元素并使用纯 JavaScript 大大提高了性能。
猜你喜欢
  • 2016-08-09
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
相关资源
最近更新 更多