Knockout.js 在半大型数据集下非常慢

IT技术 javascript performance knockout.js
2021-02-07 05:00:08

我刚刚开始使用 Knockout.js(一直想尝试一下,但现在我终于有了借口!) - 但是,在将表绑定到相对较小的一组表时,我遇到了一些非常糟糕的性能问题数据(大约 400 行左右)。

在我的模型中,我有以下代码:

this.projects = ko.observableArray( [] ); //Bind to empty array at startup

this.loadData = function (data) //Called when AJAX method returns
{
   for(var i = 0; i < data.length; i++)
   {
      this.projects.push(new ResultRow(data[i])); //<-- Bottleneck!
   }
};

问题是for上面循环大约需要 30 秒左右,大约有 400 行。但是,如果我将代码更改为:

this.loadData = function (data)
{
   var testArray = []; //<-- Plain ol' Javascript array
   for(var i = 0; i < data.length; i++)
   {
      testArray.push(new ResultRow(data[i]));
   }
};

然后for循环在眨眼间完成。换句话说,pushKnockoutobservableArray对象的方法非常慢。

这是我的模板:

<tbody data-bind="foreach: projects">
    <tr>
       <td data-bind="text: code"></td>
       <td><a data-bind="projlink: key, text: projname"></td>
       <td data-bind="text: request"></td>
       <td data-bind="text: stage"></td>
       <td data-bind="text: type"></td>
       <td data-bind="text: launch"></td>
       <td><a data-bind="mailto: ownerEmail, text: owner"></a></td>
    </tr>
</tbody>

我的问题:

  1. 这是将我的数据(来自 AJAX 方法)绑定到可观察集合的正确方法吗?
  2. 我希望push每次调用它时都会进行一些繁重的重新计算,例如重建绑定的 DOM 对象。有没有办法延迟这个重新计算,或者一次推入我的所有项目?

如果需要,我可以添加更多代码,但我很确定这是相关的。在大多数情况下,我只是按照网站上的 Knockout 教程进行操作。

更新:

根据下面的建议,我更新了我的代码:

this.loadData = function (data)
{
   var mappedData = $.map(data, function (item) { return new ResultRow(item) });
   this.projects(mappedData);
};

但是,this.projects()400 行仍然需要大约 10 秒。我承认我不确定没有Knockout(只是通过 DOM 添加行)的速度有多快,但我感觉它会比 10 秒快得多。

更新 2:

根据下面的其他建议,我尝试jQuery.tmpl(KnockOut 本身支持),这个模板引擎将在 3 秒多一点内绘制大约 400 行。这似乎是最好的方法,缺少一种可以在滚动时动态加载更多数据的解决方案。

6个回答

请参阅:Knockout.js 性能问题 #2 - 操作 observableArrays

更好的模式是获取对我们底层数组的引用,推送到它,然后调用 .valueHasMutated()。现在,我们的订阅者只会收到一个通知,表明数组已更改。

正如评论中所建议的那样。

Knockout 有它自己的与 (foreach, with) 绑定相关联的本机模板引擎。它还支持其他模板引擎,即 jquery.tmpl。阅读此处了解更多详情。我还没有对不同的引擎进行过任何基准测试,所以不知道它是否会有所帮助。阅读您之前的评论,在 IE7 中您可能很难获得您所追求的性能。

顺便说一句,KO 支持任何 js 模板引擎,如果有人为它编写了适配器。您可能想尝试其他人,因为 jquery tmpl 将被JsRender取代

我的性能越来越好,jquery.tmpl所以我会使用它。如果我有一些额外的时间,我可能会调查其他引擎以及编写自己的引擎。谢谢!
2021-03-18 05:00:08
@MikeChristensen - 您还在使用data-bindjQuery 模板中的语句,还是使用 ${ code } 语法?
2021-03-18 05:00:08
@MikeChristensen - 好的,那么这是有道理的。KO 的原生模板引擎不一定效率低下。当您使用 ${ code } 语法时,您不会在这些元素上获得任何数据绑定(这提高了性能)。因此,如果您更改 a 的属性ResultRow,它不会更新 UI(您必须更新projectsobservableArray 这将强制重新呈现您的表)。如果您的数据几乎是只读的,则 ${ } 绝对是有利的
2021-03-30 05:00:08
死灵法术!jquery.tmpl不再开发
2021-04-03 05:00:08
@ericb - 使用新代码,我使用了${code}语法,而且速度要快得多。我也一直试图让 Underscore.js 工作,但还没有运气(<% .. %>语法干扰了 ASP.NET),而且似乎还没有 JsRender 支持。
2021-04-07 05:00:08

除了使用 $.map 之外,还使用 ​​KO 进行分页

在我使用分页与淘汰赛之前,我在处理包含 1400 条记录的大型数据集时遇到了同样的问题。使用$.map加载记录确实有很大的不同,但 DOM 渲染时间仍然很糟糕。然后我尝试使用分页,这使我的数据集点亮速度更快,并且更加用户友好。页面大小为 50 使数据集不那么不堪重负,并显着减少了 DOM 元素的数量。

KO很容易做到:

http://jsfiddle.net/rniemeyer/5Xr2X/

KnockoutJS 有一些很棒的教程,特别是关于加载和保存数据的教程

在他们的情况下,他们使用getJSON()非常快的方式提取数据从他们的例子:

function TaskListViewModel() {
    // ... leave the existing code unchanged ...

    // Load initial state from server, convert it to Task instances, then populate self.tasks
    $.getJSON("/tasks", function(allData) {
        var mappedTasks = $.map(allData, function(item) { return new Task(item) });
        self.tasks(mappedTasks);
    });    
}
绝对是一个很大的改进,但是self.tasks(mappedTasks)运行大约需要 10 秒(400 行)。我觉得这还是不能接受。
2021-03-29 05:00:08
我同意 10 秒是不可接受的。使用knockoutjs,我不确定什么比地图更好,所以我会喜欢这个问题并寻找更好的答案。
2021-03-29 05:00:08
行。答案绝对值得一个+1简化我的代码并显着提高速度。也许有人对瓶颈是什么有更详细的解释。
2021-04-01 05:00:08

KoGrid看看。它可以智能地管理您的行渲染,从而提高性能。

如果您尝试使用绑定将 400 行绑定到一个表foreach,那么通过 KO 将这么多行推送到 DOM 中会遇到麻烦。

KO 使用foreach绑定做了一些非常有趣的事情,其中大部分都是非常好的操作,但是随着数组大小的增加,它们的性能确实开始下降。

我一直在尝试将大型数据集绑定到表格/网格的漫长黑暗道路上,最终您需要在本地分解/分页数据。

KoGrid做到了这一切。它被构建为仅呈现查看者可以在页面上看到的行,然后虚拟化其他行直到需要它们。我想你会发现它在 400 个项目上的表现比你正在经历的要好得多。

有趣的是,现在我们不得不勉强支持IE11。在过去的 7 年里,情况有所改善。
2021-03-21 05:00:08
很高兴研究它 - KoGrid 仍在积极开发中。但是,这是否至少回答了您关于性能的问题?
2021-03-27 05:00:08
对!这证实了我最初的怀疑,即默认的 KO 模板引擎很慢。如果您需要任何人来为您测试 KoGrid,我很乐意。听起来正是我们所需要的!
2021-03-27 05:00:08
该死的。这看起来真的很好!不幸的是,我的应用程序中超过 50% 的用户使用 IE7!
2021-03-29 05:00:08
这在 IE7 上似乎完全被破坏了(没有一个示例工作),否则这会很棒!
2021-04-02 05:00:08