24

我有一个复杂的视图模型,它是几百行带有大量可观察属性、计算可观察属性、可写计算可观察属性和函数的 javascript 代码。所以管理这个是一个相当大的挑战。

我不得不处理的一个烦人的问题是,当你定义它时,计算的 observable 会立即计算出来。因此,在定义可观察对象时使用尚未在视图模型中定义的变量会导致错误表明该变量尚未定义。它是……就在文件的后面。

这是一个人为的例子:

function ViewModel1​(args) {
    var self = this;

    self.firstName = ko.observable(args.firstName);
    self.lastName = ko.observable(args.lastName);
    self.fullName = ko.computed(function () {
        return self.firstName() + ' ' + self.lastName();
    });
}

function ViewModel2​(args) {
    var self = this;

    self.fullName = ko.computed(function () {
        // uh oh, where's firstName?
        return self.firstName() + ' ' + self.lastName();
    });
    self.firstName = ko.observable(args.firstName);
    self.lastName = ko.observable(args.lastName);
}

使用ViewModel1将毫无问题。此时fullName已定义,firstName并且lastName已定义,因此它按预期工作。使用ViewModel2将不起作用。计算函数中会出现错误,说明firstName未定义。

到目前为止,我一直在做的是确保在定义所有因变量之后定义所有计算的 observables。这样做的问题是,这样做时,事情是在看似随机的地方定义的,而我宁愿将相关变量定义在一起。

我想出的一个很好的解决方案是定义一个“正在初始化”的 observable 集,true并让所有计算出的 observable 测试它是否仍在初始化,如果不是,则计算并返回值。这样,就不会尝试访问当前未定义的变量。

function ViewModel3(args) {
    var self = this;
    var initializing = ko.observable(true);

    self.fullName = ko.computed(function () {
        if (!initializing()) {
            return self.firstName() + ' ' + self.lastName();
        }
    });
    self.firstName = ko.observable(args.firstName);
    self.lastName = ko.observable(args.lastName);

    initializing(false);
}

但这在我的情况下不是很实用。我有很多计算过的 observables,所以在所有这些中这样做会使它变得非常臃肿,记住我有很多这样的。节流它似乎没有什么区别。

有没有办法告诉淘汰赛在尝试计算计算的可观察值之前等待?或者有没有更好的方法来构建我的代码来处理这个问题?

我可能会创建一些辅助函数来管理初始化逻辑,但我仍然必须更改所有计算出的 observable 定义。我想我可以用猴子补丁淘汰赛来添加这个初始化逻辑,因为我不知道淘汰赛有这样的选项,我可能会这样做。我之前已经查看过计算的 observables 的来源,但我不知道其他地方是否已经有设置。

jsfiddle 演示

4

1 回答 1

40

Computed observables 接受一个deferEvaluation选项,该选项阻止初始评估发生,直到某些东西真正尝试检索计算的值。

你可以这样定义它:

self.fullName = ko.computed({
   read: function() {
       return self.firstName() + " " + self.lastName();
   },
   deferEvaluation: true
});

为了完整起见,您还可以像这样指定它:

this.fullName = ko.computed(function() {
       return this.firstName() + " " + this.lastName();
 }, this, { deferEvaluation: true });

或者你可以像这样包装它:

ko.deferredComputed = function(evaluatorOrOptions, target, options) {
   options = options || {};

   if (typeof evaluatorOrOptions == "object") {
       evaluatorOrOptions.deferEvaluation = true;   
   } else {
       options.deferEvaluation = true;
   }

   return ko.computed(evaluatorOrOptions, target, options);
};
于 2012-07-19T21:32:10.430 回答