使用jQuery动态生成html的正确方法

时间:2011-10-07 11:45:41

标签: jquery html performance dom

我在这个主题上找到了一些不同且相互矛盾的答案。

我正在构建一个主要使用jQuery动态生成的html的应用程序,它基于从JSON数据形式从底层API获取的结果。

我的一些同事(个人)告诉我,最好的方法是做这样的事情:

var ul = $("<ul>").addClass("some-ul");
$.each(results, function(index) {
  ul.append($("<li>").html(this).attr("id", index));
});
$("body").append($("<div>").attr("id", "div-id").addClass("some-div").append(ul));

等。 我被告知的原因是“直接更新DOM而不是解析html来实现它”。

但是,我看到很多像这样的代码(相同的例子):

var toAppend = '<div class="some-div" id="div-id"><ul>';
$.each(results, function(index) {
  toAppend += '<li id="' + index + '">' + this + '</li>';
});
toAppend += '</ul></div>'

我个人认为并不优雅 - 但它更好吗?我用Google搜索了几分钟后发现了this article。基本上,它是通过使用字符串连接来大幅提高性能 - 我的“第二种方式”。

本文的主要问题是它已于2009年发布,并讨论了jQuery版本为1.3。今天,当前版本是版本1.6.4,其行为可能完全不同。这是我已经找到的关于这个主题的大多数文章的问题,而且我对它们的可信度也有些怀疑。

这就是为什么我决定在这里发布问题并询问 - 根据性能,哪种生成DOM的方法实际上是正确的?

重要编辑:

我已经写了一些基准来测试哪种方法更好地考虑性能。

jsFiddle - 连接版

jsFiddle - 数组连接版本

代码:

var text = "lorem ipsum";
var strings = $("#strings");
var objects = $("#objects");
var results = $("#results");

// string concatenation
var start = new Date().getTime();
var toAppend = ['<div class="div-class" id="div-id1"><ul class="ul-class" id="ul-id1">'];
for (var i = 1; i <= 20000; i++) {
    toAppend[i] = '<li class="li-class" id="li-id1-' + i + '">' + text + '</li>';
}
toAppend[i++] = '</ul></div>';
results.append(toAppend.join(""));
strings.html(new Date().getTime() - start);

// jquery objects
var start = new Date().getTime();
var ul = $("<ul>").attr("id", "ul-id2").addClass("ul-class");
for (var i = 0; i < 20000; i++) {
    ul.append($("<li>").attr("id", "li-id2-" + i).addClass("li-class"));
}
results.append($("<div>").attr("id", "div-id2").addClass("div-class").append(ul));
objects.html(new Date().getTime() - start);

似乎对字符串的操作比使用jQuery对象和方法更快(在Firefox 7中大约7倍)。但我可能是错的,特别是如果这个“基准”代码中有任何错误或性能下降的错误。随意做出任何改变。

注意:由于前面提到的文章而不是实际的连接,我使用了数组join

编辑:根据@hradac的建议,我在基准测试中使用了实际的字符串连接,事实上它确实改善了时间。

5 个答案:

答案 0 :(得分:11)

首先,这种微基准测试几乎从未告诉过您想要了解的内容。其次,您的基准测试是多种多样的,而不是等同的。例如,您的第一个示例生成如下所示的行:

<li class="li-class" id="li-id1-493">lorem ipsum</li>

你的第二行是这样的:

<li id="li-id2-0" class="li-class"></li>

注意不同的元素顺序和缺少“lorem ipsum”文本。也没有任何尝试清除测试之间的结果div,以避免由于前20K结果已经出现而导致的性能问题。

但除了这些问题之外,还有一个问题,“这方面的表现是否会破坏客户端用户体验?”真的吗?您是以这种方式呈现如此大量的文本,以至于您在呈现文本的替代方法之间看到明显的差异?

我会回想别人的说法,使用模板引擎。最快的那些确实非常快,甚至有预编译选项,允许您重新渲染相同的模板并获得快速结果。所以不要相信我。相反,相信一个示威。这是我的jsFiddle,用于演示应该替换jQuery Template引擎的新JsRender库的性能......

http://jsfiddle.net/LZLWM/10/

注意:JsRender可能需要几秒钟才能加载到小提琴中。这是因为我将它直接从GitHub中拉出来并且这不是GitHub特别擅长的东西。我不建议在实际操作中。它不会改变时间,直到jsFiddle开始将模板引擎作为选项加入时才有必要。

请注意,第二个示例,更接近真实世界的示例,使用JSON作为其起始时间生成20,000行,与您的最快测试(在我的机器上差异小于50ms)大致相同。还要注意,代码和模板比任何附加的乱码和字符串连接都更加清晰和易于使用。我需要多少次迭代才能使我的模板与您正在进行的操作相对应?

使用简单的东西,并且在可能甚至没有必要的情况下停止在这种微优化级别上浪费时间。而是使用这样的模板(或任何其他好的模板引擎),并确保你已经打开过期标题,你正在使用CDN,你已经在你的服务器上启用了gzip压缩等。所有YSlow告诉你要做的事情,因为这会完全淹没你在这里看到的效果。

答案 1 :(得分:6)

答案 2 :(得分:4)

通常,我认为可读性胜过性能,除非您确实遇到问题。我会选择第二种方法,因为它很容易被识别为标准HTML片段,我认为不容易出错。

使用第一种方法,我必须解析我的minhd中的大量代码,以便想象最终字符串的样子(以及它可能需要修改的地方)。

答案 3 :(得分:2)

基本上,第一种方法使用.innerHTML的多个方法调用来创建结果,而第二种方法只使用一种方法。这是导致执行所需时间差异的主要区域。

如果字符串变得非常大,我会建议使用数组的第三种方法。

var toAppend = ['<div class="some-div" id="div-id"><ul>'];
$.each(results, function(index) {
  toAppend.push('<li id="' + index + '">' + this + '</li>');
});
toAppend.push('</ul></div>');
$(target).append(toAppend.join(""));

我通常只使用数组方法来保持一致。

编辑:hradac是对的,现在连接方法更快。

答案 4 :(得分:1)

你的第一种方法更好。我们的想法是不要在必要之前触摸DOM。在这两个示例中,您在内存中创建UL,然后在最后使用body.append()将其附加到DOM。

但是,构建树的首选方法是在第一个示例中。字符串连接有点慢(当然,我们说的是毫秒)。但是,如果每页必须多次这样做,那么这可能会变得很重要。

我会稍微清理你的代码,但只是为了便于阅读:

var div = $("<div>").attr("id", "div-id").addClass("some-div");
var ul = $("<ul>").addClass("some-ul");
$.each(results, function(index) {
    var li = $("<li>").html(this).attr("id", index);
    ul.append(li);
});
div.append(ul);

// you haven't touched the DOM yet, everything thus far has been in memory

$("body").append(div); // this is the only time you touch the DOM