Javascript knockoutjsforeach阻塞主线程

Javascript knockoutjsforeach阻塞主线程,javascript,knockout.js,Javascript,Knockout.js,当我的viewModel中有一个大数据集,并且我使用foreach在对象数组上循环以将每个对象渲染为表中的一行时,KnockoutJS将阻塞主线程,直到它可以渲染为止,这有时需要几分钟(!) 下面是一个使用包含2000个对象的数据集的JSFIDLE示例,其中包含url和code。在某些情况下,真实数据将有更长的URL和其他4列(在本例中只有2列)。我还添加了一些简单的样式,因为添加样式似乎也会在过程中减慢速度 警告:您的浏览器可能会损坏 我建议,如果您有如此大的数据集,请尝试其他解决方案。例如,

当我的viewModel中有一个大数据集,并且我使用
foreach
在对象数组上循环以将每个对象渲染为表中的一行时,KnockoutJS将阻塞主线程,直到它可以渲染为止,这有时需要几分钟(!)

下面是一个使用包含2000个对象的数据集的JSFIDLE示例,其中包含
url
code
。在某些情况下,真实数据将有更长的URL和其他4列(在本例中只有2列)。我还添加了一些简单的样式,因为添加样式似乎也会在过程中减慢速度

警告:您的浏览器可能会损坏


我建议,如果您有如此大的数据集,请尝试其他解决方案。例如,通过仅为实际可见的数据生成HTML元素,以更高效的方式呈现大型数据集。我们已经将其用于大型数据集,并且它的性能很好。

类似的东西怎么样。比如说,您已经有了想要渲染的
viewModel.items=ko.observableArray()

  • 为所有数据创建一个单独的不可观察数组:
    var itemsToRender=functionthattreturnslargerary()
  • itemsToRender
    中的部分数据放入可观察数组。比如说,只有50个元素
  • 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
    。这将需要更长的时间来处理整个收集。您还可以使用
    requestAnimationFrame
    而不是
    setTimeout
    ,并查看其效果

    编辑:已添加到(目前处于测试阶段,但似乎相当稳定)



    注2:如果视图上的某些其他数据依赖于
    viewModel.items
    ,则仍可以将其映射到原始数组
    itemsToRender
    。例如,假设您希望显示集合中的项目数。如果使用
    viewModel.items()。为了避免这种情况,您可以首先基于
    itemsToRender
    ,而不是
    viewModel.items
    ,将大小绑定定义为
    dependenttobservable
    。渲染完所有项目后,如果您认为合适,可以将其重新映射到
    viewModel.items

    这将说明问题:它减少了数据量,因为不需要强调这一点。下面是如何使用不同的方法立即渲染相同的html(2000行)。我不是说要这样做,但它只是表明淘汰不是非常优化…@Esailija-你的第一个例子实际上非常快。对于我们来说,只有当行数大于1000行时,这才是一个问题。我们一次提取所有数据的原因也有很多。所以,分页或以块的形式发送数据并不是一个真正的选项。你认为这应该作为一个bug提交吗?从技术上讲,这不是一个bug,但也许他们应该知道它。同时,您可以使用模板吐出一个字符串,并以与第二个示例相同的方式设置html(不是字面意义上的,而是使用
    .html
    而不是
    [0].innerHTML
    )。