为什么使用innerHTML向DOM添加大量元素比JQuery的append()函数慢?

4

我有一个纯JavaScript函数,用于测试向DOM添加大量元素:

var start = new Date().getTime();
var blah;
var div = document.getElementById("me");
for (i = 0; i < 5000; ++i) {
    div.innerHTML += "<div>" + i + "</div>";//Simply add to div.
}
var end = new Date().getTime();
var time = end - start;
alert('Execution time: ' + time);

结果:

         Chrome           IE10
         ------           -----  
Vanilla      39             130  (seconds)

JQuery:

for (i = 0; i < 5000; ++i) {
    $("#me").append("<div>" + i + "</div>");//Now using append instead.
}

结果

         Chrome           IE10
         ------           -----  
Vanilla   39000         130,000  (milliseconds) 
JQuery      260            1300  (milliseconds) 

NB:使用 $("#me") 选择器或传递 $(div) 看起来不会对性能产生影响。

使用 AppendChild 的原始JavaScript:

for (i = 0; i < 5000; ++i) {
    var el = document.createElement("div");//Now create an element and append it.
    el.innerHTML = i;
    div.appendChild(el);
}

             Chrome           IE10
             ------           -----  
Vanilla       39000         130,000  (ms)   
JQuery          260            1300  (ms) 
AppendChild      30             240  (ms)

我非常惊讶,这是迄今为止最快、最高效的。在Chrome上大约需要30毫秒,在IE上需要约240毫秒。
你可以在这里尝试各种变化:Fiddle 我知道还可能有许多其他的变化需要测试,但是jQuery在幕后做了什么,使得它的.append()比本地JS的innerHTML +=更快,为什么创建一个新元素并将其附加甚至更快?

1
你试过查看append的源代码吗?你确定你正在比较同类项吗?真正的问题是:你是否使用了正确的函数来快速定位元素;一旦找到结果,你是否缓存了它;这些函数(innerHTML)是否正在执行你认为它们要执行的操作(并且只执行这些操作)以及将HTML添加到DOM的最快方法是什么(可能有多种方法!)。 - George Stocker
6
appendHTML 字符串 插入到 #document_fragment 中,然后再插入到 DOM 中,而 innerHTML 每次都需要重新解析整个 DOM 部分。使用 createElement 的本机 DOM 方法比 append 更快,因为它不必解析任何 _HTML_(直到再次设置该部分的 _innerHTML_)。 - Paul S.
2
http://jsperf.com/here-we-go-again - Niet the Dark Absol
除了Cerbrus的评论之外,@Malcoda还指出,innerHTML也会删除已注册到重新解析的内容的事件处理程序。 - Paul S.
2
@NiettheDarkAbsol 除了我拒绝接受内联事件处理程序存在之外,我还拒绝接受属性可以混合大小写或alert是_JavaScript_实现中的本地函数。 </梦想世界> - Paul S.
显示剩余6条评论
3个回答

8
如果你正确处理事情,你的“最好”的结果几乎可以翻倍。
本地DOM方法始终比它们的jQuery替代方法更快。但是,.innerHTML并不理想。
当你使用.innerHTML += ...时,以下是发生的情况:
  1. 构建当前存在的整个DOM的HTML表示形式
  2. 将您的新字符串附加到其中
  3. 解析结果并从中创建一个全新的DOM树
  4. 将新内容放置在旧内容的位置
本机方法要少得多 ;)
还应该注意,innerHTML += ...完全摧毁了DOM,这意味着任何对旧元素的引用都会丢失,特别是事件处理程序不会保留(除非您使用内联事件处理程序,但您不应该这样做)。

哦,这真是讽刺。为了解决你不应该使用的代码中的“错误”,你不得不编写更多你真的不应该使用的代码(除非你使用了内联事件处理程序,但你不应该这样做)。 - Cerbrus
@Cerbrus 基本上是这样,哈哈。有时候我也会遇到“bug”。我使用了container.innerHTML = ""来清空一个元素然后重新填充它,接着尝试重新添加我已经引用的一个元素,结果在 IE 中发现它是空的,因为以这种方式清除元素还会分离子元素! - Niet the Dark Absol
即使将HTML拼接成单个字符串,然后分配为innerHTML,但在某些浏览器中,+=运算符的速度非常慢,而仅仅更改为markup = markup + '...'会产生显着的差异(当然,在某些主机中差异可能不重要,在其他主机中则不同)。 - RobG

5

2

我认为,如果您事先计算好要附加的所有内容,然后附加这些内容,会更加高效 - 最小化所需的DOM操作。例如:

var toAppend = "";
for (i = 0; i < 5000; ++i) {
   toAppend += "<div>" + i + "</div>";
}
div.append(toAppend)

如果你想要它们嵌套,你可以递归地实现,或者想出其他的解决方案。无论哪种方式,我相信字符串操作总是比DOM操作更快。


1
这是确实正确的,"较少的innerHTML调用"是更好的选择,但是"不使用innerHTML"是最好的选择;) - Niet the Dark Absol

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接