【问题标题】:Knockout subscribe is blocking the page淘汰订阅正在阻止页面
【发布时间】:2014-07-23 22:52:05
【问题描述】:

我有一个可观察的数组,它通过计算来获取当前选中的单选列表项。

self.KOVehicle.SelectedPolicy = ko.computed(function () {
        return ko.utils.arrayFirst(self.KOVehicle.VehicleDetailsList(), function (veh) { return veh.PolicyId() == self.KOVehicle.SelectedPolicyId(); });
    }, self);

我正在订阅将更新页面上十几个不同绑定项目的更改。

self.KOVehicle.SelectedPolicy.subscribe(function (selectedPolicy) {
        // show ajax loading spinner and do changes
    });

现在一切正常,只是它似乎在进行更改时阻止了页面。除非我打开 js 调试器并单步执行,否则我看不到 ajax 加载微调器。有什么想法吗?

【问题讨论】:

  • 仅仅订阅一个 observable 不会导致事情挂起,这是你在收到这些事件时所做的。您的 "show ajax loading spinner and do changes" 代码导致事情挂起...您需要在其中包含您正在执行的操作。
  • 一件不相关的事情:您可以从ko.computed 代码的末尾删除, self,因为您没有在函数中的任何地方使用this

标签: javascript knockout.js


【解决方案1】:

我想我已经看到了同样的问题。在我当前的 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

【讨论】:

  • 有趣,我想我们一定是做错了什么才导致这个问题。
  • 如果您遇到和我一样的问题,那么我认为我们根本上没有做错什么,只是可能编码不好?我相当确定我的只是由于我的 KO 模板中的一些糟糕的嵌套 foreach 循环。所以我去看看我是否可以优化这些循环——我做到了——我把它从一个 n^2 循环减少到只有 n 个(其中 n 是我循环的东西的数量)。我还记得更多关于我原来的工作,所以我要编辑我的答案很多!
猜你喜欢
  • 2013-07-24
  • 2013-11-17
  • 1970-01-01
  • 1970-01-01
  • 2012-09-20
  • 2016-02-29
  • 1970-01-01
  • 2019-03-28
  • 1970-01-01
相关资源
最近更新 更多