DOM Mutation Observers比DOM Mutation事件慢吗?

时间:2012-07-11 03:43:10

标签: javascript performance events observers mutation-events

以下代码利用DOM Mutation Event DOMNodeInserted来检测body元素的存在并将其innerHTML包装到包装器中。

<!DOCTYPE html>
<html lang="en">
<head>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>
        function DOMmanipulation() {
            if (document.body) {
                document.removeEventListener('DOMNodeInserted', DOMmanipulation);
                // DOM manipulation start
                document.body.innerHTML = '<div class="wrapper">' + document.body.innerHTML + '</div>';
                // DOM manipulation end
            }
        }
        document.addEventListener('DOMNodeInserted', DOMmanipulation);
    </script>
</head>
<body>
    <p>Lorem ipsum dolor sit amet.</p>
</body>
</html>

尽管包装成功,但是有一个错误表明找不到节点。一个问题的This answer解释说,这是因为当加载jQuery时,它在主体中添加了一个div元素来进行一些测试,但它无法删除div元素,因为它元素已被包装到包装器中,因此它不再是正文的子元素。

上面的实验告诉我们DOMNodeInserted事件比jQuery的测试更快,因为jQuery的测试元素(div)在被jQuery删除之前就被包装了。




现在,以下代码可以实现相同的操作,并且它使用新引入的DOM Mutation Observers。截至此时(2012-07-11),它仅适用于Chrome 18及更高版本。

<!DOCTYPE html>
<html lang="en">
<head>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>
        var observer = new WebKitMutationObserver(function() {  
            if (document.body) {
                observer.disconnect();
                // DOM manipulation start
                document.body.innerHTML = '<div class="wrapper">' + document.body.innerHTML + '</div>';
                // DOM manipulation end
            }
        });
        observer.observe(document, { subtree: true, childList: true });
    </script>
</head>
<body>
    <p>Lorem ipsum dolor sit amet.</p>
</body>
</html>

此代码未产生任何错误。这意味着jQuery比DOM Mutation Observers更快,因此它可以在将该元素包装到包装器之前删除其测试元素(div)。




从上述两个实验中,我们发现在执行速度方面:

  • DOM突变事件&gt; jQuery的测试
  • jQuery的测试&gt; DOM Mutation Observers

这个结果能否恰当地证明DOM Mutation Observers比DOM Mutation Events慢?

2 个答案:

答案 0 :(得分:26)

DOM Mutation Observers,并不比DOM Mutation Events更快。相反,它们旨在更高效,更安全。

差异的基本要点是DOM Mutation Events会在发生变化时触发。例如,这段代码会创建一个回调循环,最终会使浏览器崩溃。

document.addEventListener('DOMNodeInserted', function() {
    var newEl = document.createElement('div');
    document.body.appendChild(newEl);
});

事实上它们以这种方式被调用并且因此经常对浏览器产生重大影响,因为它强制浏览器之间的中断重新计算样式,重排和重新绘制循环或更糟糕地迫使浏览器重新计算样式,重排和重温每次回调。由于其他代码可能正在执行而对DOM进行进一步更改的事实进一步加剧了这个问题,这将继续被回调中断。

更重要的是,因为事件以与普通DOM事件相同的方式传播,您将开始听到您可能不关心或未在代码中考虑的元素的更改。因此,DOM Mutation事件的整个机制很快就会变得很麻烦。

DOM突变观察者通过观察DOM的变化并向您提供从变更开始时发生的所有变化的报告来抵消这些问题。这是一个更好的情况,因为它允许浏览器在有意义的时间通知您,例如当文档空闲且所有其他可以进行进一步更改的JavaScript已完成执行时,或者在浏览器重新启动之前recalc / repaint循环,因此它可以应用您所做的任何更改,而不必在之后不久重复循环。

它还使您更容易管理,因为您可以扫描所有已更改的元素以找到您要查找的内容,而不是为您不关心的内容编写大量案例处理代码,因为突变事件的情况。更重要的是它只会调用一次,所以你不必担心任何进一步的变化会影响元素,即它们不再处于改变状态,它们已经改变了。

因此,在回答您的问题时,DOM Mutation Observers更慢,因为他们在通知您jQuery更改之前等待jQuery完成对DOM的操作。由于上面解释的原因和您的示例,证明它是更安全更有效的解决方案(您不再导致错误),并且您并不真正关心jQuery为DOM添加了一些东西,因为它很快就会删除它。使用Observers,您将收到一份报告,详细说明要添加和删除的jQuery元素。

然而,这仍然有点麻烦,因为您必须通过将元素与发生的所有更改进行匹配来弄清楚实际发生了什么。实际情况是,只要你担心什么都没发生(添加和删除相同的元素),所以在DOM的结构中没有任何实际改变。为了解决这个问题,有一个名为MutationSummary的小库:

http://code.google.com/p/mutation-summary/

计算更改的净效果,并仅调用您的回调传递这些更改。因此,在您的情况下,根本不会调用您的回调,因为更改的净效果为零。

E.g。对于以下内容,您只能进行一次更改。体型改为左:1000px。即使我以1000增量更改它。变化的净效应仅是其初始值与最终值之间的差异。

function moveBody() {
    for (var i = 0; i < 1000; i++) document.body.style.left = i + 'px';
}
moveBody();

答案 1 :(得分:6)

简单的答案是变异观察者是异步的。只要发动机感觉它,发生变化后的某个时间以及在发生了很多变化之后的某些情况下,它们就会被发送。这可能是在DOM突变事件监听器通知您之后很长时间,但同时引擎可以自由地完成其工作,而不必为每个捣乱的DOM更改不断创建和冒泡事件。