我想我已经看到了同样的问题。在我当前的 KO 应用程序中,用户可以单击项目上的编辑。我加载与该项目关联的属性列表。这会触发一些 ko.computed 函数和一个带有大量嵌套 foreach 语句的大型 KO 模板。 KO 同步评估所有这些,这意味着它必须在 JavaScript 解释器返回到我的代码的下一行之前完成。正如您所描述的,我低效的 foreach 循环中的 DOM 操作花费了 2-3 秒,这造成了页面“阻塞”大约 2-3 秒的外观。
这里的问题,非常简单,作为 HTML 和 JS 的混合:
<button data-bind="click: edit">Edit</button>
// This is my list of attributes. A lot of computeds and a big template depend on
// this observable.
var list = ko.observableArray();
edit: function(data, event) {
// Update the list
list(data.list)
// Now wait 2-3 seconds for anything to happen.
}
所以为了给用户一些即时反馈,我尝试添加一个加载微调器,大致如下:
<button data-bind="click: edit">Edit</button>
// This is my list of attributes. A lot of computeds and a big template depend on
// this observable.
var list = ko.observableArray();
edit: function(data, event) {
// Update the list
list(data.list)
$(".spinner").show() // Doesn't appear.
// Now wait 2-3 seconds for anything to happen. The spinner only then appears.
}
好的,很明显我认为我可以通过换行来解决它:
<button data-bind="click: edit">Edit</button>
// This is my list of attributes. A lot of computeds and a big template depend on
// this observable.
var list = ko.observableArray();
edit: function(data, event) {
$("#spinner").show() // Still doesn't appear
// Update the list
list(data.list) // Blocks page
// Now wait 2-3 seconds for anything to happen.
}
这仍然不起作用 - 直到渲染完成后微调器才会出现。这是一个 jsfiddle 正是展示了这个问题。您可以看到页面“阻塞”的效果,因为按钮在列表呈现之前保持冻结在其活动状态。旋转器直到 KO 完成后才会出现,即使我尝试让它首先出现:
http://jsfiddle.net/6f6Rt/3/
我最初的答案建议使用超时作为解决此问题的方法,但我仍然找不到更好的方法。这是一个显示超时并使用计算的小提琴:
http://jsfiddle.net/222CG/2/
这是超时技巧:
// Show the spinner immediately...
$("#spinner").show();
// ... by using a timeout wrapped around the thing that causes the delay.
window.setTimeout(function() {
ko.applyBindings(vm)
}, 1)
在这个小提琴中,如果你放了一个
console.log("computing")
在计算内部,你会看到它被计算了 3001 次,最初一次,每次我们推入 vm.items() 的项目一次。在阅读有关提高我的应用程序性能的信息时,我遇到了油门:
http://knockoutjs.com/documentation/throttle-extender.html
这是一个显示计算油门的小提琴;还带有控制台输出;现在你可以看到它只被调用了两次,这一定是值得边际性能提升的!
http://jsfiddle.net/M4CUq/1/
关于我的代码,它被部署到平板电脑上,而且速度更慢,尤其是在 CPU 速度较慢的旧 Android 上,所以我得出结论,我真的应该优化它。我嵌套了 for-each 循环,都在列表上工作,所以基本上我有一个 O(n^2) 问题。我重写了列表数据结构,这样我就可以通过一个 foreach 循环来实现我的目标。我将 2-3 秒的延迟缩短到不到半秒。
总之:
我怀疑你做的事情根本上是错的;但是使用 Knockout 可以触发一组可以持续几秒钟的同步操作,尤其是在它包含 DOM 操作的情况下。这可以在上面的 fiddles 中看到,它使用一个简单的模板,但是一个大循环,将页面阻塞约 3 秒,
您能做的最好的事情是尝试优化您的渲染代码并限制一些计算
为了帮助进行 UX,您可以使用超时 hack 来确保在触发长 KO 操作之前可以看到微调器
哦,最后......当一切都完成后,你如何隐藏微调器?我决定使用模板绑定的 afterRender 回调,您可以在没有模板本身的情况下执行此操作,因此这里是最后的小提琴,展示了它的操作!
http://jsfiddle.net/A5tZU/
略有不同的方法1:持续阅读-
这个小提琴展示了一种使用超时将每个项目逐个推送到列表中的技术。通过在推送操作之间设置超时,DOM 逐项更新。所以整体渲染时间还是比较长的,但是用户会立即得到反馈:
http://jsfiddle.net/rosenfeld/7TwcV/1/
略有不同的方法2:另一个问题-
我刚刚回答了另一个问题,我尝试了一个 innerHTML 技巧,以大大提高呈现大型列表的性能。
Binding around 5000 records using knockout