【问题标题】:How can I speedup adding large amounts of complex HTML to the DOM如何加快向 DOM 添加大量复杂的 HTML
【发布时间】:2018-09-04 21:47:39
【问题描述】:

我有一个普通网页,它通过 AJAX 调用加载大部分内容,该调用返回大量复杂的 HTML。当我将检索到的 HTML 放入 DOM 时,它会在很长一段时间内使浏览器瘫痪(在 Chrome 上为 5 秒,在 Edge 上为 35 秒)。

我如何将 HTML 附加到 DOM 的示例:

$.ajax("example.php").done(function (response) {
    const contentElement = document.getElementById('results');
    contentElement.innerHTML = response;
});

由于应用程序的复杂性,我想不惜一切代价避免返回 JSON 并将其转换为 HTML。

奇怪的是,在插入的 HTML 已经可见之后,浏览器会被禁用一段时间。请参阅下面的时间线,在大约 5 秒长的 Parse HTML 事件发生之前,我可以在屏幕上看到 HTML(具有正确的样式)。

如何加快 HTML 的解析和附加到 DOM 的速度?

编辑:我尝试了多种浏览器和多种注入 HTML 的方法(documentFragments、innerHTML、jquery .html()、append())。所有方法都大致一样慢。

Edit2:确切的 HTML 注入可以在这个要点中看到:https://gist.github.com/Rhinni/3032e74bab0de8f40e08a3392c0243b1

【问题讨论】:

  • 你能展示一个你试图解析的 html 的例子吗?
  • @floor 我已经添加了 html 的要点,在此先感谢
  • 感谢@Jimenemex,但是当您放置多个项目(例如从循环中)并收集它时,documentFragment 很有用,以便您可以一次性将其放置在 DOM 中。但是,在我的情况下,我已经将所有 HTML 放在一起并将其放在一起。

标签: javascript html ajax dom


【解决方案1】:

简单地将您提供的 HTML 附加或插入到浏览器中似乎不会产生任何不利影响。至少在我的浏览器/计算机上对我来说。 (铬)

运行此示例,看看您是否遇到任何延迟或暂停..

See Example

    document.getElementById("test").innerHTML = toAppend;

显然这不是一个完整的测试,因为我缺少你的 CSS,并且我通过删除换行符稍微修改了你的 html,以便我可以将文本分配给文本编辑器中的变量。

如果该示例适合您,那么我们需要进一步调查来自服务器的数据,并尝试将 css 添加到等式等中。

如果示例导致延迟,则问题可能与硬件有关,可能是您没有足够的可用内存和/或 cpu 导致浏览器瘫痪。

【讨论】:

    【解决方案2】:

    第 1 部分 - 这不是代码的加载方式,只是代码无效,即使在页面上硬编码也无法工作。


    “奇怪的是,在插入的 HTML 已经可见后,浏览器会稍作瘫痪。请参阅下面的时间线,我可以在我的屏幕上看到 HTML(带有正确的样式)在大约 5 秒长的 Parse HTML 事件发生之前。”


    关于 HTML 的实用性(它的荒谬不言自明)、它的有效性(它不是)和功能性(它不是而且可能从未有过),有一些事情需要解决。

    你应该validate your HTML,因为它非常无效,但在我们开始讨论之前,当你决定验证这个烂摊子时,你需要将它分成大约 16 个部分,因为大多数在线服务都会失败或取消验证如果一次处理这么多,请尽早处理。

    以下是由于拼写错误而并非孤立问题的问题列表。这些问题重复多次。我最担心的是值和大部分变量似乎是手动定制的。希望我弄错了,您没有花费数小时来定制会阻碍而不是真正有用的值。


    1。 #IDs must be unique -- 在任何情况下都不应该在同一页面上出现重复的 #ID


    14 #accordion- 固定,14 #headingOne - 固定,7 #model,7 #type,7#brand,...

    还有更多被骗的#IDs,我将#accordion 更改为#acordion1 为14,因为每个#accordion 都必须起作用,而不仅仅是第一个。与#accordion 有直接关系的所有相关属性也需要更改,为了功能,我再次设法更改toggle-parent="#accodion。因此,有 15 个可运行的手风琴,我添加了一个带有正确设计的手风琴的主页选项卡,如果您决定重新设计其他 14 个手风琴,您可以将其用作模板。


    2。要使用 Bootstrap 组件,请按照文档制作。


    OP 代码甚至没有任何选项卡,如果您提到Bootstrap documents 甚至W3School's short tutorials,您就会知道每个选项卡都需要一个<a>,所以您的代码很短16 <a> 切换 16 个选项卡。这就是为什么您的页面只显示 16 个选项卡中的第一个选项卡的原因,并不是因为浏览器中途失败了。

    3。我注意到的另一件无效的事情是属性readonly(以及在较小程度上的required)应用于几乎所有表单控件。


    为什么需要<select> 标记上的readonly 属性? 为元素分配属性时,不要开始为所有内容添加大量属性。混乱使得可读性、维护和调试变得不可能。


    4。有 2 个 Plunk:


    1. Plunk 1OPO原始Post)问题的解决方案,详细解释在部分2 这个答案。 HTML 已部分修复,我没有足够的时间修复所有内容。

    2. 它有 16 个选项卡和 15 个可工作的手风琴。

    3. 加载时间已从 34 秒减少到 2 秒。与边缘。似乎 Edge 英勇地试图理解被解析然后失败的 HTML。 Firefox 和 Chrome 等真正的浏览器只是将其转储并留在那里。

    4. Plunk 2 是来自 OP 代码的 HTML,我的解决方案正在加载它。

    5. 结果相同,OP代码失败是因为代码本身,而不是因为加载问题。


    第 2 部分 - 一种将大字符串解析为 HTML 的稳定方法。如果 OP 代码确实有效,则不需要。


    OP 在尝试通过innerHTML 向 DOM 添加大量标记时遇到严重延迟。使用 Edge 完全渲染它最多需要 34 秒,而其他浏览器 OP 报告为 3 秒。

    我在 Edge 上将加载时间缩短到 2 到 3 秒,并在真实浏览器(Chrome 和 Firefox)上立即加载

    虽然 OP 已经尝试使用 createDocumentFragment(),但我相信它是快速加载和解析所述 HTML 的关键。 OP 可能没有使用的其他关键组件是: insertAdjacentHTML()Immediately Invoked Function Expression

    使用 insertAdjacentHTML() 方法而不是 innerHTML 属性。 insertAdjacentHTML()innerHTML 的强大且多功能的版本。

    相似之处:

    • 两者都将采用给定的字符串并解析为 HTML。

    • 两者都很快。

    区别:

    insertAdjacentHTML() 将 HTML 插入 DOM,它不会覆盖元素中或 DOM 中任何地方的任何现有 HTML。 innerHTML 覆盖元素的内部。

    innerHTML 指向一个元素,它将接受一个字符串并用给定的字符串覆盖所述元素的所有内容。如果innerHTML 只是指向一个没有字符串的元素,那么它将返回该元素的 HTML 内容。 innerHTML GET 的能力是 insertAdjacentHTML()不能做的唯一事情。相比之下,insertAdjacentHTML()SET 功能非常强大,如下所述:insertAdjacentHTML() 不仅通过引用元素被引导,它还通过其第一个参数准确地告知与引用元素相关的位置这是与 position 相关的 4 个 DOMString 之一:

    "beforebegin" 将字符串放置在元素的beginning 之前

          `$elector.before(str)`★
    

    "afterend" 将字符串放置在元素的end 之后

          `$elector.after(str)`★
    

    "afterbegin" 将字符串放在元素的右侧 brgin之后>宁。换句话说,字符串被插入到元素的内容之前。

         `$elector.prepend(str)`★
    

    "beforeend" 将字符串元素的右边before end >。基本上,字符串放在元素内容之后。这个位置对于速度是最优化的,因为没有后续的兄弟姐妹可以减慢速度。

         `$elector.append(str)`★
    

    insertAdjacentHTML() 第二个参数是要解析成 HTML 的字符串。使用Template Literals 代替文字字符串可以让我们更轻松地进行字符串操作。

        `element.insertAdjacentHTML("beforeend", <input id="${ID+i}" type="${typeArr[i]}" value="${Math.floor(Math.random() * i}">)`
    

    立即调用函数表达式是一个具有特殊模式的函数。

    • 通常是两个匿名函数:

    • 外部函数用括号括起来。

    • 内部函数通常形成closure

    • 匿名/表达式函数在求值时创建,然后由于包裹在它们周围的额外括号而立即调用它们。

    • 它们没有名字,内部函数使用的变量只能被外部函数访问,因为它们是局部作用域。

    这些条件使 IIFE 成为一次性的事情。 IIFE 的签名略有不同,但其要点如下:

    `(function() { var x = function() {...} x})();`
    

    DOM 操作是处理器密集型的,我们越避免它越好。 DocumentFragment 是为了让我们完成所有涉及 DOM 的琐碎但大量的任务——脱离 DOM。我们可以向 DocumentFragment 及其后代添加尽可能多的元素、文本、属性、设置事件处理程序等,而无需接触 DOM。一切完成后,只需要进行一次 DOM 操作:

     `document.body.appendChild(frag);` 
    

    演示 - 如果您想测试实际工作的演示,请查看此Plunk

    <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset='utf-8'>
      <title>Monstrosity</title>
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css" />
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" />
    </head>
    
    <body>
    
      <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/js/bootstrap.min.js"></script>
      <script>
        (function() {
          const str = `Huge Disaster of HTML`
          const frag = document.createDocumentFragment();
          const node = document.createElement('div');
          let build = function(node, str, frag) {
            node.insertAdjacentHTML('beforeend', str);
            frag.appendChild(node);
            document.body.appendChild(frag);
          }
          build(node, str, frag);
        }());
      </script>
    </body>
    
    </html>

    【讨论】:

    • 感谢您的详尽回答。您对重复 ID 是正确的,但是,虽然这是不好的做法,但它不会破坏 HTML 的解析和呈现。我修复了没有真正性能差异的重复 ID。稍后我仍会尝试您的其他建议以进一步提高性能,但我解决了真正的性能问题(请参阅我自己的答案)。
    【解决方案3】:

    我在 Trevor 和 Zer00ne 的回答的帮助下解决了这个问题,但情况完全不同。

    这个问题是由Laravel Debugbar 引起的,它默认跟踪 AJAX 请求,并解析响应以进行调试。在 Debugbar 配置中禁用 AJAX 请求跟踪解决了这个问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多