【问题标题】:Why does removing elements take more time than adding elements?为什么删除元素比添加元素需要更多时间?
【发布时间】:2018-02-19 08:30:20
【问题描述】:

我有一个包含很多行的表(例如 10.000)。
(注意:我意识到拥有这么大的表没有意义,但我想了解以下行为)。
当我收到新数据时,我想先清除行。奇怪的是,清理表比从头开始构建行需要更长的时间。使用 html("") 或普通 JS innerHTML = "" 清除表行需要 1.5 分钟,这比构建行本身花费的时间不到一秒要多得多。

所以我的问题:
- 为什么删除元素比添加元素需要更多时间? “幕后”会发生什么?
(请注意,我的问题是为什么,我不是在寻找可能的解决方法)。

更新

我注意到表格行和单元格应用了浮动定义,当我删除浮动时,表格在一个实例中被清空。
我仍然很想了解为什么浮点数会使删除行的速度慢得多。
这里有一个小提琴:https://jsfiddle.net/maaikeb/t5pduuue/
在 Chrome 中,清空表格需要 25 秒,而将其附加到 DOM 只需 23 ms

*我阅读了下面的帖子,但他们更多地讨论了可能的解决方案,而不是为什么删除元素比添加它们需要更多时间,当你删除它们时实际会发生什么

jQuery empty is very slow
jQuery empty is very slow
What is the best way to remove a table row with jQuery?
Deleting table rows in javascript
jquery - fastest way to remove all rows from a very large table

【问题讨论】:

  • 如果要替换所有行,为什么不直接删除整个表?我认为这个问题很好,需要一个答案,但我的意思是你的问题现在你可能应该考虑它:)
  • @GaneshRadhakrishnan 请在下面查看我的评论 - 显然显示有这么多行的表格没有意义,我将添加分页。我仍然想了解为什么删除元素比添加元素更需要性能
  • 在哪个浏览器中?
  • 你能重现这个小提琴吗? jsfiddle.net/yqc92zqd

标签: javascript jquery html performance dom


【解决方案1】:

我假设你在 IE 上表现不佳。

我会说这是因为$.empty 方法在循环中为表中的每个已删除元素调用removeChild,并且该方法在 IE 中的性能总是很差 - 请参阅this article for short case study 以及一些性能比较和解决方案。

无论您如何处理大型 DOM,您都会在 IE 上表现不佳。 This article on appendChild in large DOM on IE 是社区仍然被这个简单事实所迷惑的一个例子。 :)

【讨论】:

  • 其实这不是在 IE 中,而是在 Chromium 和 Firefox ESR 上都发生过
  • 能否请您提供两者的确切版本?因为他们过去曾被 removeChildaddChild 的相同问题所困扰。
  • Firefox ESR 45.9.0 和 Chromium 57.0.2987.98 谢谢
  • 你能创建一个小提琴或codepen吗?曾经在这两种浏览器上都有相当多的错误,例如Chrome 的 removeChildvery slow on css-heavy page. 我无法真正重现 - 在 Chrome 上对我来说大约是 200 毫秒。
  • 你说得有道理 - 我试图创建一个重现场景的小提琴,但它不会重现。在我的代码中进一步挖掘,我发现如果我删除一个使所有 td 元素浮动的类,问题也得到了解决。我试图重现后者,但也没有成功 - 我需要进一步挖掘
【解决方案2】:

您可以使用 hide() 和 show(),而不是循环整个 DOM 删除并再次重新渲染。由于检索隐藏列也非常容易,因此速度快且性能好。请发现该片段很有用。

//did based on checkbox here update to ur requirement hide() callback remains same.

$("input:checkbox:not(:checked)").each(function() {
    var column = "table ." + $(this).attr("name");
    $(column).hide();
});

$("input:checkbox").click(function(){
    var column = "table ." + $(this).attr("name");
    $(column).toggle();
});
td, th {
    padding: 6px;
    border: 1px solid black;
}

th {
    background-color: #f0eae2;
    font-weight: bold;
}

table {
    border: 1px solid black;
}
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<p><input type="checkbox" name="first_name" checked="checked"> First Name 
  <input type="checkbox" name="last_name"> Last Name 
  <input type="checkbox" name="email" checked="checked"> Email</p>

<table id="report">
<thead>
<tr>
 <th class="first_name">First Name</th>
 <th class="last_name">Last Name</th>
 <th class="email">Email</th>
</tr>
</thead>
<tbody>
<tr>
 <td class="first_name">Larry</td>
 <td class="last_name">Hughes</td>
 <td class="email">larry@gmail.com</td>
</tr>
<tr>
 <td class="first_name">Mike</td>
 <td class="last_name">Tyson</td>
 <td class="email">mike@gmail.com</td>
</tr>
</tbody>

【讨论】:

    【解决方案3】:

    您可以通过为其内部 HTML 分配空字符串的值来清除表格。然后它不会遍历每个行元素来查找标题、tbody 或行来删除它。因此会比您当前的方法更快.

    $("#yourtableid").html("");

    【讨论】:

      【解决方案4】:

      如果你问为什么清空表格比追加表格要花费 1000 倍的时间,好吧,它不应该:这是一个错误。

      你可以发现重要的性能差异取决于浏览器、它的版本、jQuery 或操作系统的版本,但 25 秒和 23 毫秒实在是太多了。似乎比您在某个地方遇到了O(N^2) 操作。低于这个数字的人打开票证并且浏览器的开发者承认他们。

      无论如何,删除元素并不总是比添加元素更快。我们的直觉告诉我们 destroy 应该比 create 快,但在操作 DOM 时,这不一定是正确的。 jQuery 和浏览器都需要做额外的工作才能取出元素。

      如果您查看jQuery append() function 的当前(3.2.1)实现,您会发现它的基本作用是使用本机JS 方法appendChild()。另一方面,jQuery empty() functionhtml() 使用 empty())需要遍历所有内部元素,并注意删除每个内部元素的相关数据(例如事件)以防止内存泄漏。

      而且,如果你只使用 vanilla JS,浏览器在删除元素时仍然需要检查一大堆东西。如果你想了解浏览器的内部结构,可以查看Firefox issue

      不管你选择removeChild()innerHtml还是textContent(),浏览器总是需要重新计算样式和布局:检查this one in Chromium

      这种错误很常见。如果你在 Chromium 中搜索关键字“removeChild”和“slow”的问题,你会得到一个漂亮的long list。有时它们来来去去是由于引入回归的修复,比如在 this case 中涉及浮动元素。 This other one 对您来说可能特别有趣,因为它证明某些错误与您选择删除元素的方法无关,并且因为就像您的情况一样,它会在将复杂的 CSS 规则应用于该元素时出现(建议比浏览器触发重排)。

      我无法在 Firefox 和 Chrome 中重现您的问题,因此我无法解决 25 秒或 1.5 分钟的确切原因,但这似乎是最近版本中修复的错误。如果您仍然拥有相同的旧版本并且可以复制它,则可能值得检查以相反的顺序(从lastChild 开始)删除元素是否有帮助。或许您避免在移除后重新计算所有浮动兄弟姐妹的位置。

      下一次,您可能想开一张票并按照他们如何找到错误并修复它。它会满足你的好奇心,你可以学到很多关于浏览器内部的知识!

      【讨论】:

      • 感谢您的详尽回答!你是对的,这是一个 Chromium 特定的错误。我正在使用 Chromium 57.0.2987.98,在 Firefox 上它不会发生。您在具有大量 CSS 的元素上与 removeChild 链接的错误似乎引起了非常相似的问题,只是它似乎已被修复。
      • @Kemeia。我专门在问题跟踪器中查找版本 57 和 61 之间的错误(我的,它工作顺利),但我找不到它。也许它作为其他问题的一部分得到了修复,也许它从未被报告过。如果您更新到 61 或更高版本但仍有问题,则问题可能出在其他地方。如果发生这种情况,请告诉我,我们可以进一步研究!
      【解决方案5】:

      你只是想知道为什么。但也许其他人想知道一种加快速度的方法......所以我只想指出我遇到了什么。

      我在删除之前动态创建的元素时遇到了问题(是 svg 动画的一部分……有点)。 我的解决方案是将之前创建的元素存储在一个数组中。后来为了删除,我只需要遍历数组来删除它们。那要快得多,并且对我有用。 最后,就我而言,这不是 .remove()(或 .removeChild())函数本身的性能,而是对指定元素的 DOM 扫描(如 Davids 回答中所述)减慢了进程。

      【讨论】:

        猜你喜欢
        • 2021-03-23
        • 2013-06-06
        • 2016-04-30
        • 2019-01-04
        • 2017-09-25
        • 2013-11-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多