2011-11-16 55 views
4

当我在我的viewModel中有一个大的数据集,并且我使用foreach来遍历一个对象数组来将每个对象呈现为一个表内的一行时,KnockoutJS会阻塞主线程直到它可以呈现,有时需要几分钟(!)。KnockoutJS foreach块主线程

以下是使用包含2000对象的数据集的jsFiddle示例,其中包含urlcode。实际数据在某些情况下会有更长的URL,而在另外4列(本例中只有2列)中,我也添加了一些简单的样式,因为添加样式似乎也会在这个过程中减慢速度。

警告:您的浏览器威力休息

http://jsfiddle.net/DESC3/7/

+0

这将说明这个问题:http://jsfiddle.net/DESC3/10/它已经减少了数据量,因为它不需要表达点。 – Esailija

+1

下面是如何使用不同的方法http://jsfiddle.net/DESC3/11/立即呈现相同的html(2000行)。我不是说要这样做,但它只是表明淘汰赛不是非常优化... – Esailija

+0

@Esailija - 你的第一个例子其实很快。对于大于1000行的我们来说,这只是一个问题。同样,我们也有一些理由将所有数据都纳入其中。因此,分页或以块形式发送数据不是真正的选择。 – RyanScottLewis

回答

1

我建议,如果你有这样大的数据集,你尝试另一种解决方案。例如,slickGrid通过仅为实际可见的数据生成HTML元素,以更有效的方式呈现大型数据集。我们已经将它用于大型数据集,并且性能良好。

1

这样的事情呢?说,你有viewModel.items = ko.observableArray(),你想呈现。

  1. 有一个单独的不可观察数组的所有数据:var itemsToRender = functionThatReturnsLargeArray()
  2. itemsToRender中的部分数据放入可观察数组中。说只有50个元素。
  3. 保持将元素添加到setTimeout回调中的可观察数组中。

注1:你可以添加一些时间跟踪到setTimeout回调和增加/减少您添加在每次迭代的项目数。您的目标是将每个回调时间保持在50-100毫秒以下,以便您的应用程序仍能感觉到响应。

var batchSize = 50; // default number of items rendered per iteration 
var batchOffset = 0; 
function render(items, itemsToRender, done) { 
    setTimeout(function() { 
     var startTime = new Date().getTime(); 
     items.pushAll(itemsToRender.slice(batchOffset, batchSize)); 
     batchOffset += batchSize; 
     // at this point Knockout rendered next batchSize items from itemsToRender 
     var endTime = new Date().getTime(); 
     // update batchSize for next iteration 
     batchSize = batchSize * 50/(endTime - startTime); // 50 milliseconds 
     batchSize = Math.min(itemsToRender.length, batchOffset + batchSize); 
     if (batchSize > 0) render() else done(); // callback if you need one 
    }, 0); 
} 
/* I haven't actually tested the code */ 

另一批量更新策略可以基于目标FPS。假设您希望获得60 fps的更新速率,因此每1000毫秒需要60个电话给setTimeout。处理整个收藏需要更长的时间。您也可以使用​​而不是setTimeout并查看如何解决问题。

编辑Build-in throttling加入到Knockout JS 1.3(目前它在测试阶段,但似乎相当稳定)。


注2:如果认为其他一些数据取决于viewModel.items你仍然可以映射下来到原数组itemsToRender。比如说,你想显示收藏中的物品数量。如果您使用viewModel.items().length,则最终会在UI中更改大小值,同时会渲染更多项目。为了避免这种情况,您可以首先根据itemsToRender定义您的尺寸绑定为dependentObservable,而不是viewModel.items。完成渲染所有项目后,如果您觉得合适,可将其重新映射到viewModel.items