使用 jQuery 动态生成 html 的正确方法
我在这个话题上发现了一些不同且相互矛盾的答案。
我正在构建一个应用程序,该应用程序主要使用 jQuery 动态生成的 html,基于从底层 API 以 JSON 数据形式获取的结果。
我的一些同事(个人)告诉我,最好的方法是做这样的事情:
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>'
我个人认为这不太优雅 - 但它更好吗?我在谷歌上搜索了这个问题几分钟,找到了这篇文章。基本上,它是通过使用字符串连接来大幅提高性能 - 我的“第二种方法”。
本文的主要问题是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 倍)。但我可能是错的,特别是如果这个“基准”代码中存在任何错误或性能下降的错误。请随意进行任何更改。
注意:由于前面提到的文章,我使用了 Array join
而不是实际的串联。
编辑:根据@hradac的建议,我在基准测试中使用了实际的字符串连接,它实际上确实改善了时间。
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.
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".
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.
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?
IMPORTANT EDIT:
I have written a little benchmark to test which approach is better considering performance.
jsFiddle - concatenation version
jsFiddle - array join version
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.
Note: I used Array join
because of the article mentioned earlier instead of actual concatenation.
EDIT: Based on suggestion by @hradac, I used actual string concatenation in the benchmark and it did in fact improve the times.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
首先,这种微基准测试几乎永远不会告诉你你真正想知道的东西。其次,你们的基准是多种多样的,而且并不等同。例如,您的第一个示例生成如下所示的行:
第二行如下所示:
请注意不同的元素顺序以及缺少“lorem ipsum”文本。也没有尝试清除测试之间的结果 div,以避免由于前 20K 结果已经存在而导致的性能问题。
但除了这些问题之外,还有一个问题:“这方面的性能真的会破坏客户端用户体验吗?”严重地?您以这种方式呈现如此大量的文本,以至于您发现呈现文本的替代方法之间存在明显差异?
我会回顾一下其他人所说的,使用模板引擎。最快的确实非常快,甚至具有预编译选项,允许您重新渲染相同的模板并获得快速结果。所以别相信我。相反,相信示威。这是我的 jsFiddle,用于演示新的 JsRender 库的性能,该库应该取代 jQuery 模板引擎...
http: //jsfiddle.net/LZLWM/10/
注意:JsRender 可能需要几秒钟的时间才能加载到 Fiddle 中。这是因为我直接从 GitHub 中提取它,而这并不是 GitHub 特别擅长的事情。我不建议在实际操作中这样做。但它不会改变时间,并且在 jsFiddle 开始将模板引擎作为选项合并之前这是必要的。
请注意,第二个示例更接近真实世界的示例,使用 JSON 作为其起始时间点生成 20,000 行,与最快的测试大致相同(在我的计算机上相差 < 50 毫秒)。另请注意,代码和模板比任何混乱的附加和字符串连接都更清晰、更容易使用。与您正在做的事情相比,我需要多少次迭代才能使我的模板正确?
使用简单的东西,并停止在这种级别的微观优化上浪费时间,甚至可能没有必要。相反,请使用这样的模板(或任何其他好的模板引擎),并确保您已打开过期标头、您正在使用 CDN、您已在服务器上打开 gzip 压缩等。 YSlow 告诉您要做的事情,因为这将完全淹没您在这里看到的效果。
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:
and your second lines like this:
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.
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...
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.
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?
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.
查看 jquery 模板 -
http://api.jquery.com/category/plugins/templates/< /a>
Check out jquery templates -
http://api.jquery.com/category/plugins/templates/
一般来说,我认为可读性胜过性能,除非您确实遇到问题。我会选择第二种方法,因为它很容易被识别为标准 HTML 片段,而且我认为不太容易出错。
使用第一种方法,我必须解析 minhd 中的大量代码,以想象最终的字符串会是什么样子(以及可能需要修改的地方)。
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.
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).
你的第一种方法更好。我们的想法是除非必要,否则不要接触 DOM。在这两个示例中,您都在内存中创建 UL,然后最后使用
body.append()
将其附加到 DOM。但是,构建树的首选方法是第一个示例。字符串连接有点慢(当然,我们说的是毫秒)。但是,如果您必须在每个页面上多次执行此操作,那么这可能会变得很重要。
我会稍微清理一下你的代码,但只是为了可读性:
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()
.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:
基本上,第一个方法使用对 .innerHTML 的多个方法调用来创建结果,而第二个方法仅使用一个方法。这是导致执行时间差异的主要区域。
如果字符串变得非常大,我建议使用数组的第三种方法。
我通常使用数组方法只是为了保持一致。
编辑:hradac 是对的,连接方法现在更快。
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.
I would suggest a 3rd method using an array if the strings get extremely large.
I generally use the array method only to be consistant.
Edit: hradac is right, the concatenate method is faster now.