【问题标题】:Poor performance when adding large number of elements to page向页面添加大量元素时性能不佳
【发布时间】:2019-07-25 22:22:19
【问题描述】:

我制作了一张表格,但我使用的是<div>s 而不是<tr>s 和<td>s。这是一个例子:

<div class="tbl tbl1">
    <div class="thead">
        <div class="tr">
            <div class="td colTitle" style="width: 120px"><span>Title</span></div>
            <div class="td colLink" style="width: 190px"><span>Link</span></div>
            <div class="td colSize numeric" style="width: 75px"><span>Size(MB)</span></div>
            <div class="td colUploadDate" style="width: 75px"><span>UploadDate</span></div>
            <div class="td colOpen" style="width: 50px; max-width: 50px;"><span>Show</span></div>
        </div>
        <div class="tr">
            <div class="td colTitle">
                <input type="text" class="Filter" />
            </div>
            <div class="td colLink">
                <input type="text" class="Filter" />
            </div>
            <div class="td colSize">
                <input type="text" class="Filter" />
            </div>
            <div class="td colUploadDate">
                <input type="text" class="Filter" />
            </div>
            <div class="td colOpen">
            </div>
        </div>
    </div>
    <div class="tbody">
    </div>
</div>

我将用 ajax 函数填充tbody。从数据库中获取所有数据后,我将其存储到客户端的数组中。我使用下面的代码使用数组arr 填充我的表。但是当我有大量行时,在我的表中显示行需要很长时间。

var res = "";
arr.forEach(function (row) {
    res += "<div class='tr' idattachment='" + row["IdAttachment"] + "' >" +
            "<div class='td colTitle'>" + row["Title"] + "</div>" +
            "<div class='td colLink'>" + row["Name"] + "</div>" +
            "<div class='td colSize'>" + (row["Size"] / (1024 * 1024)).toFixed(2) + "</div>" +
            "<div class='td colUploadDate'>" + row["UploadDate"] + "</div>" +
            "<div class='td colOpen'><a class='link' href='uploads/" + row["Name"] + "'>Open</a></div>" +
            "</div>";
});
$(".tbody").html(res);

有没有更高效的方式将数据加载到表中?

【问题讨论】:

  • 您应该考虑使用模板引擎和/或分页。
  • 我不想使用分页选项。
  • 如何“逐行”执行,中间稍作延迟?这样加载时间会更长,但不会冻结。
  • 鉴于您将所有 html 构建到一个字符串中并且只执行一个追加,如果这需要“很长时间”才能显示,这反映了您尝试的数据量显示
  • 如果你真的需要展示一个巨大的表格,首先,放弃jQuery,使用HTMLTable Interface,它已经被优化为处理表格。避免向表格元素添加类或 ID,或者实际上是任何属性。还要避免繁重的样式,使用colgroup/col 元素和/或样式表(nth-child 选择器)来设置元素的样式。您可以测试 documentFragment 追加行和单元格是否比直接在页面上追加更快,或者尝试追加到 display:none 表,您可以在它准备好后显示。

标签: javascript jquery html arrays


【解决方案1】:

在加载时给浏览器一些喘息的空间。

var nextRow = 0;
var handler;


//Process a row in every 250ms
var handler = setInterval(function() {

   //Select next row
   var row =arr[nextRow];

   var res= "<div class='tr' idattachment='" + row["IdAttachment"] + "' >" +
            "<div class='td colTitle'>" + row["Title"] + "</div>" +
            "<div class='td colLink'>" + row["Name"] + "</div>" +
            "<div class='td colSize'>" + (row["Size"] / (1024 * 1024)).toFixed(2) + "</div>" +
            "<div class='td colUploadDate'>" + row["UploadDate"] + "</div>" +
            "<div class='td colOpen'><a class='link' href='uploads/" + row["Name"] + "'>Open</a></div>" +
            "</div>";

   //Append
   $(".tbody").appendChild(res);

   //Move on
   nextRow++;

   //Exit at the end
   if (arr.length === nextRow)
      clearInterval(handler);

}, 250);

这将继续将您的数据添加到表中,直到最后。

还要记住,字符串连接很昂贵。您甚至可以尝试不带间隔的appendChild 方法。

【讨论】:

  • 这将允许一些喘息的空间,但会导致浏览器执行额外的 DOM 和 CSS 布局工作,这实际上可能会更慢。表的构建应该在没有您的 setInterval 的情况下进行,但在添加最后一行之后使用 appendChild。
  • 浏览器不会死机。整体操作会比OPs方法流畅很多。
  • 准备好后显示每一行是个好主意。但它对速度问题没有帮助。
  • 我没有说浏览器会死机。浏览器甚至可能会感觉运行顺畅,但由于重新渲染,浏览器最终会更加努力地工作。
  • 浏览器是为了更努力地工作:)。 OP 想让它更快更流畅。如果他觉得可怜的东西累了,他可以关闭浏览器。 ;)
【解决方案2】:

使用虚拟渲染仅渲染实际在视图中的行。

Clusterize.js 为例:

var rows = [];
for (var i = 0; i < 10000; i++) {
  rows.push('<tr><td>Row ' + i + '</td></tr>');
}

var clusterize = new Clusterize({
  rows: rows,
  scrollId: 'scrollArea',
  contentId: 'tbody'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/clusterize.js/0.18.0/clusterize.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/clusterize.js/0.18.0/clusterize.min.css" rel="stylesheet"/>

<div class="clusterize-scroll" id="scrollArea">
  <table>
      <thead class="thead">
          <tr class="tr">
              <td>
                <span>Title</span>
              </td>
          </tr>
      </thead>
      <tbody id="tbody" class="clusterize-content">
        <tr class="clusterize-no-data">
          <td>Loading data…</td>
        </tr>
      </tbody>   
  </table>
</div>

【讨论】:

    【解决方案3】:

    我将进行性能分析。我会一步一步来,所以如果你喜欢这种模式,你可以尝试做同样的事情。请记住,有时这种分析会表明您无能为力。

    绩效目标

    当您尝试存档关卡性能时,第一个问题应该是我们要做什么。

    正常比例如下。

    • 100-300ms 轻微的可察觉延迟
    • 300-1000ms 正在完成一项任务。
    • > 失去焦点 1 秒。
    • > 10s 沮丧和放弃任务

    看来您正在执行一项任务,所以

    在关注这些目标时,请遵循数据。

    JS

    您正在使用 forEach 似乎是最好的选择。 https://jsperf.com/for-vs-foreach/75

    String Plus 与模板文字,似乎无关紧要。 https://jsperf.com/template-literal-vs-string-plus/7

    点表示法与方括号表示法似乎无关紧要。 https://jsperf.com/dot-notation-vs-square-bracket-notation/5

    JS 结论

    支持您的 Javascript 的数据已尽可能优化。

    CSS

    未提供 CSS。

    CSS 结论

    不确定

    渲染

    jQuery append vs html vs innerHTML

    https://jsperf.com/jquery-append-vs-html-list-performance/24

    似乎使用 innerHTML 会是更好的选择。

    渲染结论

    将 .html 更改为 innerHTML

    感知

    当我在没有 CSS 的情况下呈现此代码时,因为您没有提供它。当我渲染 10,000 个项目时,我开始达到 1 秒的渲染时间。

    您可以对渲染进行分页,但如果这样做,您将需要加载渲染时间小于 16 毫秒的块。即使你认为你没有动画滚动也是一个动画。

    当您在低端设备上进行测试并获得可以在 1 秒内渲染多少项目时,请执行以下操作。当我在我的设备上击中 1 秒时,我正在使用 10k。 10,000 / 1000 * 15 这将为您提供在不中断动画的情况下可以在 1 秒内加载的项目数。

    添加一个 Spinner 或显示工作正在完成的东西。因此,用户认为某些东西正在工作,它的时间从

    感知结论

    分页

    添加 Spinner 或显示正在完成的工作。

    这就是我的全部。祝你好运。

    【讨论】:

      猜你喜欢
      • 2015-10-12
      • 1970-01-01
      • 1970-01-01
      • 2012-03-17
      • 2020-10-02
      • 1970-01-01
      • 2012-12-19
      • 1970-01-01
      • 2023-01-09
      相关资源
      最近更新 更多