推迟对Knockout中所有计算属性的评估?

时间:2013-05-01 17:20:11

标签: knockout.js

我正在尝试为我的所有视图模型创建一个基类,所以我可以检查root-vm上的“isDirty”并让它检查整个vms树(同时避免循环依赖)。

执行此操作时,我意识到ko-properties的顺序非常重要,因为计算属性根据创建属性时存在的其他属性来设置其订阅。因此,如果我将一个像下面的那样的isDirty-computed放在一个基类中,它总是会在它所依赖的属性之前进行初始化。无论如何,为了缩短它,我注意到我可以使用deferEvaluation来解决这个问题。

我的问题是:

在所有计算属性上使用deferEvaluation是否有任何缺点?为什么默认情况下这种行为是正确的?我什么时候需要它设置为假?

我可以这样做更好吗?有关改进或以其他方式进行的任何建议吗?

或者,有没有办法显式禁用计算属性的初始化,直到创建具有其所有属性的整个对象,然后以某种方式运行它。我的意思是对我来说唯一的问题是订阅是在所有属性到位之前设置的。

注意:我正在使用KO Lite工具进行脏跟踪

function ViewModel() {
    var self = this;
    self.isDirty = ko.computed(function () {
        for (var p in self) {
            if (self[p].isDirty) {
                if (self[p].isDirty()) return true;
            }
            else if (self[p].subscribe && self[p].push) { // assuming ko.observableArray
                for (var i = 0, j = self[p]().length; i < j; i++) {
                    if (self[p]()[i].isDirty) {
                        if (self[p]()[i].isDirty()) return true;
                    }
                }
            }
        }
        return false;
    }, this, { deferEvaluation: true });
}

注意:注意,如果我有另一个依赖于isDirty的计算属性,此代码将失败。这有点令人期待,但也很不幸。如果我可以在创建对象后延迟并强制所有订阅,那就太好了。

2 个答案:

答案 0 :(得分:3)

正如您所发现的那样,通常会计算ko.computed,并在首次创建计算时检测到依赖关系。

使用deferEvaluation时,不会立即执行ko.computed的依赖性检测,而是在每次请求值时执行。{

这提供了一种动态改变依赖关系的强大机制,但确实会增加开销。

Ryan谈到这一点on this page(向下滚动到第3节 - 计算可观察量的基本规则):


是否可以构建代码,以便在创建其他属性后定义 的值{em}?

如果您的主要问题是脏标记跟踪,您可以在基本VM中设置beginInit()和endInit()方法,并在endInit()中设置isDirty计算。您或者可能不需要beginInit(),但它提供了一个很好的钩子以保持一致性,以后可能会有用。

当然,派生的VM需要在设置其observable之前和之后调用基本init方法。我不确定您当前创建派生VM的策略是什么 - 我通常使用工厂来提供原型继承,并且我对初始化时间有这种控制。

beginInit()和endInit()的一个更简单的替代方法是简单地在派生类中提供initProperties()函数,其中定义了所有属性,并从基本VM调用该方法,然后设置isDirty计算

答案 1 :(得分:2)

如果您的computed确实是一个计算,那么推迟评估通常没有任何损害,直到某人真正需要该值。

如果您的computed实际上是作为获取价值或其唯一目的的副产品而执行某项操作,则推迟评估可能不是正确的选择。在某些情况下,computed可用作订阅多个可观察对象的方式,并在其中任何一个更改而不是简单地返回值时执行操作。

在创建整个对象之前,除了延迟评估并稍后访问之外,没有办法禁用初始化,除非您要在setTimeout中创建计算机。如果其他代码希望它出现在那里,这可能会导致错误。