KnockoutJS foreach 阻塞主线程
当我的 viewModel 中有一个大型数据集并且我使用 foreach
循环对象数组以将每个对象渲染为表中的一行时,KnockoutJS 将阻塞主线程,直到它可以渲染为止。有时需要几分钟(!)。
这是一个 jsFiddle 示例,使用包含 2000 个对象的数据集,其中包含 url
和 code
。在某些情况下,真实数据将具有更长的 URL 和其他 4 列(本例中只有 2 列)。我还添加了一些简单的样式,因为添加样式似乎也会在此过程中减慢速度。
警告:您的浏览器可能崩溃
When I have a large dataset in my viewModel and I use foreach
to loop over an Array of Objects to render each Object as a row within a table, KnockoutJS will block the main thread until it can render, which sometimes takes minutes (!).
Here is a jsFiddle example using a dataset containing 2000 Objects, containing a url
and a code
. Real data will have longer URLs in some cases and 4 other columns (only 2 in this example.) I also added some simple styles because adding styles also seems to slow things down a bit during the process.
Warning: your browser might break
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我建议如果您有如此大的数据集,请尝试替代解决方案。例如,slickGrid 通过仅为数据生成 HTML 元素,以更有效的方式呈现大型数据集。实际上可见。我们已将其用于大型数据集,并且表现良好。
I suggest that if you have such large datasets you try an alternative solution. For example slickGrid renders large datasets in a much more efficient way, by only generating HTML elements for the data that is actually visible. We've used this for large datasets, and it performs well.
像这样的事情怎么样?比如说,您想要渲染
viewModel.items = ko.observableArray()
。var itemsToRender = functionThatReturnsLargeArray()
。itemsToRender
中的部分数据放入可观察数组中。假设只有 50 个元素。setTimeout
回调中不断将元素添加到可观察数组中。注意1:您可以在
setTimeout
回调中添加一些时间跟踪,并增加/减少每次迭代时添加的项目数量。您的目标是将每次回调时间保持在 50-100 毫秒以下,以便您的应用程序仍然具有响应能力。另一种批量大小更新策略可以基于目标 FPS。假设您希望达到 60 fps 的更新速率,因此每 1000 毫秒调用
setTimeout
60 次。处理整个集合需要更长的时间。您还可以使用requestAnimationFrame
代替setTimeout
并查看效果如何。编辑:构建-in throttting 已添加到 Knockout JS 中1.3(目前处于测试阶段,但看起来相当稳定)。
注意2:如果视图上的某些其他数据依赖于
viewModel.items
,您仍然可以将其映射到原始数组itemsToRender
。例如,假设您想要显示集合中的项目数。如果您使用 viewModel.items().length,您最终会在 UI 中更改大小值,同时更多项目会被渲染。为了避免这种情况,您可以首先将大小绑定定义为基于itemsToRender
的dependentObservable
,而不是viewModel.items
。渲染完所有项目后,如果您认为合适,可以将其重新映射到viewModel.items
上。How about something like this. Say, you've got
viewModel.items = ko.observableArray()
that you'd like to render.var itemsToRender = functionThatReturnsLargeArray()
.itemsToRender
into your observable array. Say, 50 elements only.setTimeout
callback.NOTE1: You can add some time-tracking into
setTimeout
callback and increase/reduce the number of items that you add at each iteration. Your goal is to keep each callback time below 50-100 milliseconds so your application still feels responsive.Another batch size updating strategy could be based on target FPS. Say you'd like to achieve 60 fps update rate and thus 60 calls to
setTimeout
per 1000 milliseconds. That would take somewhat longer to process the whole collection. You can also userequestAnimationFrame
instead ofsetTimeout
and see how that would work out.EDIT: Build-in throttling was added into Knockout JS 1.3 (currently it's in beta but seems pretty stable).
NOTE2: If some other data on the view depends on
viewModel.items
you can still map it down to original arrayitemsToRender
. Say, for example, that you'd like to show the number of items in collection. If you useviewModel.items().length
you'll end up with changing size value in the UI while more items get renderred. To avoid that you can first define your size binding as adependentObservable
based onitemsToRender
, notviewModel.items
. After you've done rendering all items you can re-map it ontoviewModel.items
if you see fit.