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

时间:2021-03-15 13:13:04

I found some different and conflicting answers on this topic.

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

I am building an application which works mostly with html dynamically generated by jQuery, based on results acquired from underlying API in form of JSON data.

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

I was told by some of my collegues (personally), that the best way would be to do something like this:

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

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));

etc. The reason I was told it was that "updates the DOM directly instead of parsing html to achieve it".

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

However, I see lots of code like this (same example):

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

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

Which I personally consider as not as elegant - but is it better? I googled the issue for a couple of minutes and found this article. Basically, it is about increasing performance drastically by using string concatenation - my "second way".

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

The main issue of this article is that it has been released in 2009 and discussed jQuery version is 1.3. Today, the current release is version 1.6.4 which can behave quite differently. And this is the issue of most articles on the subject I have already found and I'm also somehow suspicious about their credibility.

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

That's why I have decided to post the question here and ask - which method of generating DOM is actually the proper one, based on performance?

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

IMPORTANT EDIT:

重要编辑:

I have written a little benchmark to test which approach is better considering performance.

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

jsFiddle - concatenation version

jsFiddle - 连接版本

jsFiddle - array join version

jsFiddle - 数组连接版本

Code:

码:

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);

It seems that operating on strings is faster (in Firefox 7 about 7 times) than using jQuery objects and methods. But I can be wrong, especially if there are any mistakes or performance-decreasing bugs in this "benchmark's" code. Feel free to make any changes.

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

Note: I used Array join because of the article mentioned earlier instead of actual concatenation.

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

EDIT: Based on suggestion by @hradac, I used actual string concatenation in the benchmark and it did in fact improve the times.

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

5 个解决方案

#1


11  

First of all, this kind of micro-benchmarking almost never tells you what you want to really know. Second of all, your benchmarks are varied and not equivalent. For example, your first example generates lines that look like this:

首先,这种微观基准测试几乎从不告诉你你真正想知道什么。其次,您的基准测试是多种多样的,而不是相同的。例如,您的第一个示例生成如下所示的行:

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

and your second lines like this:

你的第二行是这样的:

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

Notice the different element order and the lack of "lorem ipsum" text. Nor is there any attempt to clean out the results div between tests to avoid performance issues as a result of the first 20K results already being there.

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

But beyond these issues is the question, "Is performance on this really disrupting the client side user experience?" Seriously? You're rendering such a quantity of text this way that you're seeing noticeable differences between the alternative methods rendering the text?

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

I'll harken back to what others have said, use a templating engine. The fastest ones are quite quick indeed and even have pre-compilation options to allow you to re-render the same template and get quick results. So don't believe me. Instead, believe a demonstration. Here's my jsFiddle to demonstrate the performance of the new JsRender library that is supposed to replace the jQuery Template engine...

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

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

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

Note: It can take several seconds for JsRender to load into the Fiddle. It's because I'm pulling it straight out of GitHub and that's not something that GitHub is particularly good at. I don't recommend that in actual practice. It doesn't change the timings though and it's necessary until jsFiddle starts incorporating templating engines as options.

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

Notice that the second example, much closer to a real-world example generates 20,000 lines using JSON as its starting point in time approximately the same as your fastest test (< 50ms difference on my machine). Note also that both the code and the template are much clearer and easier to work with than any mess of appends and string concatenation is ever going to be. How many iterations am I going to need to get my template right vs. what you're doing?

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

Use something simple and stop wasting time on this level of micro optimization when it's probably not even necessary. Instead use templates like this (or any of several other good templating engines) and make sure that you've got expires headers turned on, you're using a CDN, you've got gzip compression turned on on your server, etc. All the stuff that YSlow is telling you to do, because that will completely swamp the effects of what you're looking at here.

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

#2


6  

Check out jquery templates -

查看jquery模板 -

http://api.jquery.com/category/plugins/templates/

http://api.jquery.com/category/plugins/templates/

#3


4  

Generally, I think readability trumps performance unless you are actually having issues. I would opt for the second approach as it is easily recognizable as a standard HTML snippet, and I think less prone to errors.

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

With the first method, I have to parse a lot of code in my minhd just to imagine what the final string will look like (and thus where it might need modifications).

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

#4


2  

Basically, the first method uses multiple method calls to .innerHTML to create the result while the second method only uses one. This is the primary area that causes the difference in the amount of time that it takes to execute.

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

I would suggest a 3rd method using an array if the strings get extremely large.

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

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(""));

I generally use the array method only to be consistant.

我通常只使用数组方法是一致的。

Edit: hradac is right, the concatenate method is faster now.

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

#5


1  

Your first approach is better. The idea is not to touch the DOM until you have to. In both examples, you create the UL in memory and then at the end you attach it to the DOM with body.append().

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

However, the preferred way to build a tree is in your first example. The string concatenation is a bit slower (of course, we're talking milliseconds). However, if you have to do this many times per page, that could become significant.

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

I'd clean up your code a bit, but only for readability:

我会稍微清理你的代码,但仅限于可读性:

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

#1


11  

First of all, this kind of micro-benchmarking almost never tells you what you want to really know. Second of all, your benchmarks are varied and not equivalent. For example, your first example generates lines that look like this:

首先,这种微观基准测试几乎从不告诉你你真正想知道什么。其次,您的基准测试是多种多样的,而不是相同的。例如,您的第一个示例生成如下所示的行:

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

and your second lines like this:

你的第二行是这样的:

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

Notice the different element order and the lack of "lorem ipsum" text. Nor is there any attempt to clean out the results div between tests to avoid performance issues as a result of the first 20K results already being there.

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

But beyond these issues is the question, "Is performance on this really disrupting the client side user experience?" Seriously? You're rendering such a quantity of text this way that you're seeing noticeable differences between the alternative methods rendering the text?

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

I'll harken back to what others have said, use a templating engine. The fastest ones are quite quick indeed and even have pre-compilation options to allow you to re-render the same template and get quick results. So don't believe me. Instead, believe a demonstration. Here's my jsFiddle to demonstrate the performance of the new JsRender library that is supposed to replace the jQuery Template engine...

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

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

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

Note: It can take several seconds for JsRender to load into the Fiddle. It's because I'm pulling it straight out of GitHub and that's not something that GitHub is particularly good at. I don't recommend that in actual practice. It doesn't change the timings though and it's necessary until jsFiddle starts incorporating templating engines as options.

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

Notice that the second example, much closer to a real-world example generates 20,000 lines using JSON as its starting point in time approximately the same as your fastest test (< 50ms difference on my machine). Note also that both the code and the template are much clearer and easier to work with than any mess of appends and string concatenation is ever going to be. How many iterations am I going to need to get my template right vs. what you're doing?

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

Use something simple and stop wasting time on this level of micro optimization when it's probably not even necessary. Instead use templates like this (or any of several other good templating engines) and make sure that you've got expires headers turned on, you're using a CDN, you've got gzip compression turned on on your server, etc. All the stuff that YSlow is telling you to do, because that will completely swamp the effects of what you're looking at here.

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

#2


6  

Check out jquery templates -

查看jquery模板 -

http://api.jquery.com/category/plugins/templates/

http://api.jquery.com/category/plugins/templates/

#3


4  

Generally, I think readability trumps performance unless you are actually having issues. I would opt for the second approach as it is easily recognizable as a standard HTML snippet, and I think less prone to errors.

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

With the first method, I have to parse a lot of code in my minhd just to imagine what the final string will look like (and thus where it might need modifications).

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

#4


2  

Basically, the first method uses multiple method calls to .innerHTML to create the result while the second method only uses one. This is the primary area that causes the difference in the amount of time that it takes to execute.

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

I would suggest a 3rd method using an array if the strings get extremely large.

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

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(""));

I generally use the array method only to be consistant.

我通常只使用数组方法是一致的。

Edit: hradac is right, the concatenate method is faster now.

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

#5


1  

Your first approach is better. The idea is not to touch the DOM until you have to. In both examples, you create the UL in memory and then at the end you attach it to the DOM with body.append().

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

However, the preferred way to build a tree is in your first example. The string concatenation is a bit slower (of course, we're talking milliseconds). However, if you have to do this many times per page, that could become significant.

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

I'd clean up your code a bit, but only for readability:

我会稍微清理你的代码,但仅限于可读性:

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