如何加快向 DOM 添加大量复杂 HTML 的速度

San*_*ler 1 html javascript ajax dom

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

示例如何将 HTML 附加到 DOM:

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

由于应用程序的复杂性,我希望避免必须返回 JSON 并将其转换为 HTML。

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

时间线表现

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

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

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

zer*_*0ne 5

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

\n\n
\n\n
\n

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

\n
\n\n


\n有一些事情需要解决,涉及 HTML 的实用性(它的荒谬性本身就说明了一切)、它的有效性(它不是)和功能性(它不是,而且可能从来没有)。

\n\n

您应该验证您的 HTML,因为它非常无效,但在我们开始讨论之前,当您决定验证这一混乱的内容时,您需要将其分成大约 16 个部分,因为大多数在线服务都会提前终止或终止验证过程如果一次要处理这么多数据。

\n\n

以下问题列表并非因拼写错误而导致的孤立问题。这些问题被重复多次。最让我担心的是,这些值和大多数变量似乎都是手工定制的。希望我是错的,并且您没有花费数小时来定制会阻碍而不是有任何实际用途的值。\n


\n\n

1. #IDs 必须是唯一的ID——在任何情况下,同一页面上都不应出现重复的# 。

\n\n
\n\n

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

\n\n

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


\n\n

2. 为了使用Bootstrap组件,您需要根据文档制作它们。

\n\n


\nOP 代码甚至没有接近任何选项卡,如果您参考Bootstrap 文档甚至W3School 的简短教程<a>,您就会知道每个选项卡都需要有一个,因此您的代码缺少 16<a>来切换 16 个选项卡。这就是为什么您的页面只显示 16 个选项卡中的第一个选项卡,而不是因为浏览器中途失败。\n

\n\n

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

\n\n


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

\n\n


\n\n

4. 有 2 个小插曲:

\n\n
\n\n
    \n
  1. Plunk 1是OP原始帖子的解决方案该问题在本答案的第 2 部分中有详细解释。HTML 已部分修复,我没有足够的时间来修复所有内容。

  2. \n
  3. 它有 16 个选项卡和 15 个可用的手风琴。

  4. \n
  5. 加载时间从 34 秒减少到 2 秒。与边缘。看来 Edge 英勇地尝试理解已解析的 HTML,然后失败了。像 Firefox 和 Chrome 这样真正的浏览器只是转储它并将其留在那里。

  6. \n
  7. Plunk 2是来自OP代码的 HTML,我的解决方案加载它。

  8. \n
  9. 结果是相同的,OP代码失败是由于代码本身,而不是因为加载问题。

  10. \n
\n\n
\n\n

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

\n\n


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

\n\n

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

\n\n

尽管OP已经尝试使用createDocumentFragment(),但我相信它是快速加载和解析所述HTML的关键。OP 可能没有使用的其他关键组件是:\n insertAdjacentHTML()立即调用函数表达式

\n\n

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

\n\n

相似之处:

\n\n
    \n
  • 两者都会接受给定的字符串并解析为 HTML。

  • \n
  • 两者都很快。

  • \n
\n\n

差异:

\n\n

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

\n\n

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

\n\n

“beforebegin”将字符串放置 开始之前元素

\n\n
      `$elector.before(str)`\xe2\x98\x85\n
Run Code Online (Sandbox Code Playgroud)\n\n

“afterend”将字符串放置末尾之后在元素

\n\n
      `$elector.after(str)`\xe2\x98\x85\n
Run Code Online (Sandbox Code Playgroud)\n\n

“afterbegin”将字符串放置元素brgin之后的位置。换句话说,字符串被插入到元素内容之前。

\n\n
     `$elector.prepend(str)`\xe2\x98\x85\n
Run Code Online (Sandbox Code Playgroud)\n\n

“beforeend”将字符串放置元素end之前。基本上,字符串放置在元素内容之后。这个位置对于速度来说是最优化的,因为没有其他兄弟姐妹可以减慢速度。

\n\n
     `$elector.append(str)`\xe2\x98\x85\n
Run Code Online (Sandbox Code Playgroud)\n\n

insertAdjacentHTML()第二个参数是将被解析为 HTML 的字符串。使用模板文字而不是文字字符串使我们能够更轻松地进行字符串操作。

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

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

\n\n
    \n
  • 它通常是两个匿名函数:

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

  • \n
  • 内部函数通常形成一个闭包

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

  • \n
  • 它们没有名称,内部函数使用的变量只能由外部函数访问,因为它们是本地范围。

  • \n
\n\n

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

\n\n
`(function() { var x = function() {...} x})();`\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
 `document.body.appendChild(frag);` \n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

\r\n
\r\n
      `$elector.before(str)`\xe2\x98\x85\n
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n