2013-05-01 96 views
7

我想为我所有的viewmodels做一个基类,因此我可以检查root-vm上的“isDirty”并让它检查vms的整个树(同时避免循环依赖)。推迟评价?

当我这样做时,我意识到ko属性的顺序非常重要,因为计算属性根据创建属性时存在的其他属性设置其订阅。所以,如果我把一个isDirty计算的像一个基类,将它实际上还要考虑即使存在属性之前总是初始化下面的一个。总之,要长话短说,我发现我可以使用deferEvaluation来解决这个问题。

我的问题是:

有没有对所有的计算性能使用deferEvaluation的任何缺点?为什么默认情况下这种行为是真实的?我什么时候需要将它设置为false?

我能做到这一点isDirty属性更好?任何有关改进或以其他方式进行的建议?

另外,是否有任何方法显式禁用计算属性的初始化,直到创建具有其所有属性的整个对象,然后以某种方式运行它。我的意思是我的唯一问题是,订阅成立之前,所有属性都到位。

注:我使用的是KO精简版工具脏跟踪

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另一个计算财产这个代码将会失败。这是预料之中的,但也是不幸的。如果我可以延迟并强制所有订阅创建对象,那将是一件好事。

回答

3

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

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

这提供了一个动态改变依赖性的强大机制,但确实会导致开销增加。

瑞恩谈到这一点on this page(向下滚动到第3节 - 计算观测的基本规则):


是否可以组织你的代码,以便ko.computed值后确定其他属性是否创建?

如果您的主要问题是在脏标志跟踪中,您可以在您的基本VM中设置beginInit()和endInit()方法,并在endInit()中设置您计算的isDirty。你也可能不会需要BeginInit在(),但它提供了一个漂亮的勾手一致性和可能是有用的以后。

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

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

+1

我最终在每个类构造的末尾使用了一个endInit方法。我还必须提供调用endInit的类的显式名称,以便只有继承链的“最顶级”类实际上在执行endInit。这不是理想的,但它的工作原理。 – 2013-05-07 14:39:43

2

如果您的computed确实是一种计算,那么延迟评估通常没有任何坏处,直到有人真正需要价值。

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

没有办法禁用初始化,直到整个对象被创建,而不是通过延迟评估并稍后访问它,除非您要做一些事情,比如在setTimeout中创建计算。这可能会导致错误,但如果其他代码期望它在那里。

+0

你是否有一个具体的例子,当我需要一种方法来订阅多个观察对象并在它们中的任何一个发生变化时执行一个动作而不是简单地返回一个值。我有很多'计算',它们给出了相当复杂的计算值,但是我不知道它们中的任何一个如果推迟是真的就会中断。我正在考虑一个助手来创建一个defered = true的计算结果,但不想在没有完全理解它会变坏的情况下 – 2014-07-15 02:51:01

+1

@Simon_Weaver - 这将是一种情况,计算结果只是为了对变化做出反应到一个或多个可观测值而不是返回一个值。例如,你可以创建一个访问对象中所有可观察对象的计算器('ko.toJS(this)'可以做到这一点),并在有任何改变时调用某种类型的存储到数据库中。所以,计算是实现这一目标的简单方法,而不是尝试针对对象层次结构中的每个可观察对象创建手动预订。 – 2014-07-15 03:22:20

+0

好吧,所以基本上你说我已经知道,如果我这样做;-)我想我仍然不完全明白为什么'deferred = true'不是默认值。我认为在构造对象后立即触发每个延迟是很好的,可以立即暴露任何错误,但有时会遇到我想要全部抑制它们的情况(如映射插件在映射对象时所做的那样) – 2014-07-15 03:27:26