【问题标题】:Proper way to generate html dynamically with jQuery使用 jQuery 动态生成 html 的正确方法
【发布时间】:2011-12-02 23:02:15
【问题描述】:

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

我正在构建一个应用程序,该应用程序主要使用由 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>'

我个人认为它不那么优雅 - 但它更好吗?我用谷歌搜索了几分钟,找到了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 的建议,我在基准测试中使用了实际的字符串连接,它确实改善了时间。

【问题讨论】:

  • 如果你做了很多这样的事情,你可能要考虑使用 jquery 模板。它是专门为解决这个问题而构建的。 api.jquery.com/category/plugins/templates
  • @NimChimpsky 我知道这篇文章,它是我每天开发 jQuery 的指导方针之一。它涵盖了我的部分问题,但没有说明哪种约定更好。
  • 我建议实际使用字符串连接而不是数组连接。过去数组连接速度更快,但情况发生了变化,现在情况并非如此。 jsperf.com/array-join-vs-string-connect
  • 您忘记在 jQuery 示例中设置文本。添加.text(text) 会明显变慢,但确实展示了 jQuery 可以关闭 XSS 向量的优势。

标签: jquery html performance dom


【解决方案1】:

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

<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 模板引擎的新 JsRender 库的性能...

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

注意:JsRender 加载到 Fiddle 可能需要几秒钟。这是因为我直接从 GitHub 中提取了它,而这并不是 GitHub 特别擅长的。我不建议在实际操作中这样做。但它不会改变时间,并且在 jsFiddle 开始将模板引擎作为选项合并之前是必要的。

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

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

【讨论】:

  • 谢谢,这正是我正在寻找的答案。澄清一下:
  • 我知道我所谓的基准测试中有明显的错误,我写得不是很仔细,并且有 cmets 指出。我只是没有时间来纠正它。
  • 我只是想跟进我对 JsRender 的使用。我之所以选择它,是因为我在另一个环境中使用它。请不要在这个时候(2011 年 10 月)使用它,因为它还没有准备好投入使用。其他几个模板库中的任何一个都是更好的选择,并且在它们中的大多数之间切换并不难,因为它们中的许多具有相似的构造,只是调用它的语法略有不同。
  • 你让我“使用模板引擎”。从 2011 年开始更新,AngularJSKnockout 是我最信任的两个。
  • 在 2014 年 1 月回到这个问题(由于有人对此进行了投票)我想我会说我长期以来一直使用 Handlebars 作为 JavaScript 的模板引擎并且对此非常满意.如果您只需要一个模板引擎,我会选择它。如果您想要的不仅仅是一个模板引擎,我已经使用 AngularJS 将近一年了,并且对此也非常满意。
【解决方案2】:

【讨论】:

  • "注意:此插件作为 BETA 发布,现已弃用,不再积极开发或维护。文档暂时保留在这里(供参考),直到准备好合适的替换模板插件。”
  • 这是一个旧答案,不再是理想的解决方案。 jQuery 已弃用模板支持(如果尚未完全删除)。模板引擎是一毛钱。目前我正在使用 DOM sn-ps 进行客户端模板,但理想的解决方案完全取决于要解决的问题。
  • 此链接已失效!
【解决方案3】:

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

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

【讨论】:

  • 嗯,我现在没有问题,但是我正在开发的应用程序必须处理越来越多的数据,所以它可能会发展成问题。
  • @Przemek 也许是使用第二种方法的另一个原因,尽管我不能确认它提供了更好的性能。不过,很容易测试自己。
【解决方案4】:

您的第一种方法更好。这个想法是在你不得不触碰 DOM 之前不要触碰 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

【讨论】:

    【解决方案5】:

    基本上,第一种方法使用对 .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 是对的,concatenate 方法现在更快了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-13
      • 1970-01-01
      • 2020-02-27
      • 1970-01-01
      • 1970-01-01
      • 2021-05-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多