使用JavaScript高效地执行多个DOM更新

使用JavaScript高效地执行多个DOM更新,javascript,performance,dom,Javascript,Performance,Dom,我有一个包含数百个div元素的HTML/JS网站。其中几十个元素应一次快速更新(每秒最多更新250次)(即,它们都应一次更新,而无需浏览器通过逐个执行更新来执行不必要的工作)。什么是最有效的方法(最小化CPU负载和内存消耗)来实现这一点?支持现代浏览器使用纯JavaScript或简单的库(不使用或类似的库要求我修改DOM处理之外的代码)来实现这一点?我正在寻找类似的东西(其中imaginaryLibrary是我不知道的图书馆,我正在寻找): vari,元素; 对于(i=0;i

我有一个包含数百个div元素的HTML/JS网站。其中几十个元素应一次快速更新(每秒最多更新250次)(即,它们都应一次更新,而无需浏览器通过逐个执行更新来执行不必要的工作)。什么是最有效的方法(最小化CPU负载和内存消耗)来实现这一点?支持现代浏览器使用纯JavaScript或简单的库(不使用或类似的库要求我修改DOM处理之外的代码)来实现这一点?我正在寻找类似的东西(其中imaginaryLibrary是我不知道的图书馆,我正在寻找):

vari,元素;
对于(i=0;i<20;i++){
元素=document.getElementById('el'+i);
collectDomUpdate(元素,cssClass,'updatedcsslass');
}
//执行之前在循环中收集的DOM更新,并在一次回流中应用DOM更新
imaginaryLibrary.applyUpdate();

要更新的元素的父元素包含数千个不应更新的元素。

似乎您已经掌握了要执行的基本操作。对于您的特定用例,我认为没有理由不简单地实现这些方法

var imaginaryLibrary = function() {
    var toProcess = [];

    function collectDomUpdate(el, prop, newValue) {
        toProcess.push([el, prop, newValue])
    }

    function applyUpdates() {
        while (toProcess.length) {
            var actions = toProcess.pop()
            actions[0][actions[1]] = actions[2]
        }
    }
}

上面的代码是您可能要执行的操作的框架,它不包含错误检查,并且直接分配给对象,而不是潜在地使用DOM属性。

您需要“脱机”执行DOM更新。根据您的需求,它可能与使用display一样:在父容器的CSS中无,执行更新,然后取消隐藏所述容器。通过从DOM中删除节点并在操作后重新插入节点,可以更手动地执行类似操作


如果在元素的显示中导致可察觉的或其他不可接受的间隙,那么您可能需要考虑一个解决方案,即复制节点、操作它们,然后在适当的位置重写较旧的节点。可能需要小心,多做一些研究,以确保这不会导致浏览器一直实例化一堆新元素。也许是一个节点的“池”

你并不确切地知道你在做什么,我们在这里能做的最好的事情就是给你一些相关领域的一般性建议

DOM修改

您并没有完全填写您尝试执行的操作的所有细节,但在优化代码工作方式时,需要考虑以下几点:

  • 回流已排队。浏览器已尝试最小化回流,因此如果在一段连续的Javascript中进行四次dom修改,浏览器将等待该段Javascript完成运行,然后进行一次回流和一次重新绘制

  • 请求某些属性可能会触发立即回流。上述规则存在例外情况,您需要确保避免。例如,如果您请求某些DOM属性,这些属性需要正确的布局才能准确报告属性值,并且之前的修改存在挂起的布局,则浏览器可能会在返回您请求的属性之前同步重新发布文档。这些类型的属性通常涉及屏幕位置和其他属性,这些属性显然会受到文档布局的影响。如果您想了解更多详细信息,有很多关于这个主题的文章。在许多情况下,您的代码无论如何都不会使用这些属性,但如果是这样,通常的解决方法是在对DOM进行任何更改之前,首先请求所有需要的属性

  • 一次批处理所有DOM更改。最糟糕的做法是进行DOM更改,使用计时器等待几毫秒,再进行一次DOM更改,使用计时器等待几毫秒,依此类推,因为您将进行DOM更改、回流、重绘、DOM更改、回流、重绘等。。。相反,请确保在一段同步Javascript中同时执行所有挂起的DOM更改。这将允许浏览器对回流和重新绘制进行排队,并且在您完成所有DOM更改后只执行一次。如果您想更聪明地处理批处理,您将折叠对同一元素的修改,以便只使用最终值处理一次。因此,如果elementA首先被赋予一个新值3,然后在同一批中被赋予一个值4,那么在处理该批数据时,您希望跳过3,只处理4

  • DOM修改有时是可以优化的。您没有给我们任何关于如何修改DOM的细节,但是如果您要修改复杂对象(如添加许多表行或更改许多表单元格),那么有时最好创建一组DOM元素,这些元素当前未插入DOM中,对它们进行所有修改,然后通过一个操作将它们插入DOM。这是因为修改当前插入到DOM中的DOM元素会迫使浏览器找出哪些其他DOM元素受到此更改的影响,以便它可以对适当的回流或重新绘制进行排队。但是,在最后一步将一个更大的对象插入DOM之前,修改屏幕外DOM元素不必做任何这些工作。这里没有有用的通用规则,因为您如何利用这种优化完全取决于您所做的DOM修改。我们可以在这方面帮助您,但只有当我们能够看到您拥有的HTML以及您对它所做的更改时

  • 更新的时间安排

    现在,关于事情的时间,你提到了每秒250次。对于用户可查看的东西来说,这有点快(每次操作4ms)。如果你那样做,你就
    var imaginaryLibrary = function() {
        var toProcess = [];
    
        function collectDomUpdate(el, prop, newValue) {
            toProcess.push([el, prop, newValue])
        }
    
        function applyUpdates() {
            while (toProcess.length) {
                var actions = toProcess.pop()
                actions[0][actions[1]] = actions[2]
            }
        }
    }