Javascript性能 - Dom Reflow - Google文章

时间:2009-12-04 01:18:12

标签: javascript performance

有人可以向我证明,在更改dom元素然后重新插入dom元素之前,给出here(下面复制)的建议会更快。

通过证明,我想看一些数字。他们很好地研究了这一点,但我认为这篇文章非常薄弱,没有详细说明“问题”究竟是什么以及解决方案如何修复速度方面(如文章标题加速JavaScript)

文章......

流程外DOM操作

这种模式允许我们创建多个元素并将它们插入到DOM中,从而触发单个重排。它使用了一种叫做DocumentFragment的东西。我们在DOM之外创建一个DocumentFragment(因此它不在流程中)。然后,我们创建并添加多个元素。最后,我们将DocumentFragment中的所有元素移动到DOM,但触发单个重排。 问题

让我们创建一个函数来更改元素中所有锚点的className属性。我们可以通过简单地遍历每个锚点并更新它们的href属性来实现。问题是,这可能会导致每个锚点重新流动。

function updateAllAnchors(element, anchorClass) {
  var anchors = element.getElementsByTagName('a');
  for (var i = 0, length = anchors.length; i < length; i ++) {
    anchors[i].className = anchorClass;
  }
}

解决方案

要解决此问题,我们可以从DOM中删除元素,更新所有锚点,然后将元素插回到原来的位置。为了帮助实现这一目标,我们可以编写一个可重用的函数,不仅可以从DOM中删除元素,还可以返回一个函数,将元素插回到原始位置。

/**
 * Remove an element and provide a function that inserts it into its original position
 * @param element {Element} The element to be temporarily removed
 * @return {Function} A function that inserts the element into its original position
 **/
function removeToInsertLater(element) {
  var parentNode = element.parentNode;
  var nextSibling = element.nextSibling;
  parentNode.removeChild(element);
  return function() {
    if (nextSibling) {
      parentNode.insertBefore(element, nextSibling);
    } else {
      parentNode.appendChild(element);
    }
  };
}

现在我们可以使用这个函数更新一个不在流中的元素中的锚点,并且只在我们删除元素时和插入元素时才触发重排。

function updateAllAnchors(element, anchorClass) {
  var insertFunction = removeToInsertLater(element);
  var anchors = element.getElementsByTagName('a');
  for (var i = 0, length = anchors.length; i < length; i ++) {
    anchors[i].className = anchorClass;
  }
  insertFunction();
}

5 个答案:

答案 0 :(得分:5)

你会发现很难从javascript分析中获得有意义的数据,因为你真的在保存重绘和重新流动,这些都不会出现在大多数分析工具中。您可以使用Firebug paint events extension直观地向您显示您要保存的重绘数量。

答案 1 :(得分:2)

我在页面上放置了一些链接并测试了文章中的方法,而不是使用仍在页面中的元素设置类名。我在Firefox 3,IE 8和Chrome 3中试过这个。

我为具有不同颜色和不同字体大小的链接创建了类。由于链接文本对于不同的类具有不同的大小,我确信页面确实必须被重排。

对于任何合理数量的链接(最多几千个),删除和添加元素的速度会稍慢。

对于极大数量的链接(10 000),删除和添加元素的速度要快一些。

然而,差异很小。您必须有几千个链接才能发现任何差异,并且在10 000个链接上仍然只有20%的差异。

所以,我发现你不能指望这种方法有任何重大改变。如果您遇到性能问题,可能还有其他方法可以提供更好的结果。我会尝试更改父元素的类名而不是所有子元素,并让CSS完成工作。我之前做过的测试表明,这可以快十倍。

答案 2 :(得分:2)

这与使用documentFragments初始化元素而不是dom大致相同。文档片段最终会更快,因为它们可以减少结构和实际渲染的担忧。

以下是John Resig关于文档片段(目前在jquery中使用)的性能优势的一些注释:

http://ejohn.org/blog/dom-documentfragments/

答案 3 :(得分:1)

简短的回答是,对实际页面的DOM的更改会触发Javascript事件,CSS评估,更改的传播会影响对其周围的其余DOM的解释等。断开连接的断开连接的节点没有那样的与它们相连,对它们的操纵要便宜得多。

一个类比:如果你是一名正在玩玩具总动员4的动画师,并且在近乎最终的渲染中你看到了你需要在一个场景的面料物理中做出的改变,你会在做全部细节的同时进行改变吗?重新渲染以检查场景,或在进行这些更改时关闭纹理和颜色并降低分辨率?

答案 4 :(得分:0)

除了查看页面外,没有可靠的方法来判断回流完成的时间。

计算所花费的时间几乎与一次添加或更改元素或逐个添加或更改元素相同。该脚本不会等待浏览器之间的浏览器,浏览器会赶上。

有时候你想立刻看到一些东西,即使它需要更长的时间来渲染整个东西 - 有时候你想要等待一段时间并在它准备就绪时展示它。

如果你不知道哪个更好,请让设计师查看逐个版本和一次性版本 - 但不要问两个设计师!