【问题标题】:array semantics for computed value计算值的数组语义
【发布时间】:2012-05-14 10:50:50
【问题描述】:

我有一个 ko.computed() 函数来代理这样的集合:

function ListView(query) {
    var self = this;

    this.query = query;
    this.items = ko.observableArray([]);
    ko.computed( function() {
        self.items( self.query() == null ? [] : self.query().postings() );
    }

    // more code
}

不幸的是,这会导致 self.items() 变量随着对 posts() 数组中每个对象的每次更改而更改。我宁愿做的是有一个计算值项目直接代表查询的帖子。但如果我这样做:

 self.items = ko.computed(function() { return self.query().postings(); });

就 Knockout 而言,该值变为标量,而不是数组。这很重要的原因是,当列表中的某些项目更改状态时,我只想更新显示的那些部分,而不是重新渲染整个列表。这不仅仅是性能优化(尽管数百个项目的性能也是一个问题),而且我还试图记录当前正在显示的项目以跟踪用户可能看到的内容。

我想从逻辑上讲,我想要这样的东西:

  self.items = self.query().postings;

但是如果查询发生变化,这会中断,因为(我认为)依赖项存在于不同的对象上。

有没有一种干净的方法来构建依赖机制并仍然保留集合语义?

编辑:2012 年 5 月 13 日

我尝试实现@MichaelBest 建议的扩展方法,但产生了以下错误:

Uncaught TypeError: Object function dependentObservable() {
        if (arguments.length > 0) {
            set.apply(dependentObservable, arguments);
        } else {
            return get();
        }
    } has no method 'valueWillMutate'
ko.utils.arrayForEach.ko.observableArray.fn.(anonymous function)    knockout-2.1.0.debug.js:1087
SubviewModel.self.refresh.self.doSort    SummaryViewModel.js:73
ko.ignoreDependencies    knockout-deferred-updates.js:172
subFnObj.(anonymous function).newCallback    knockout-deferred-updates.js:188
ko.subscribable.fn.notifySubscribers    knockout-2.1.0.debug.js:870
ko.utils.arrayForEach    knockout-2.1.0.debug.js:85
ko.subscribable.fn.notifySubscribers    knockout-2.1.0.debug.js:866
evaluateImmediate    knockout-deferred-updates.js:300
evaluatePossiblyAsync    knockout-deferred-updates.js:246
ko.subscribable.fn.notifySubscribers    knockout-2.1.0.debug.js:870
ko.utils.arrayForEach    knockout-2.1.0.debug.js:85
ko.subscribable.fn.notifySubscribers    knockout-2.1.0.debug.js:866
ko.observable.observable.valueHasMutated    knockout-2.1.0.debug.js:946
observable    knockout-2.1.0.debug.js:934
updateViewModel    knockout.mapping-latest.debug.js:514
changes    knockout.mapping-latest.debug.js:389
visitPropertiesOrArrayEntries    knockout.mapping-latest.debug.js:569
updateViewModel    knockout.mapping-latest.debug.js:374
ko.mapping.fromJS    knockout.mapping-latest.debug.js:91
Topic.load    querium.js:1212
...

【问题讨论】:

    标签: knockout.js


    【解决方案1】:

    您可以创建一个计算的 observable,它返回一个数组并像 observableArray 一样工作:

    self.items = ko.computed({
        read: function() { return self.query().postings(); }
        write: function(value) { self.query().postings(value); }
    });
    ko.utils.extend(self.items, ko.observableArray.fn);
    

    现在您可以将observableArray 的所有功能与items 一起使用。

    编辑:此方法仅在您还向对象添加 valueWillMutatevalueHasMutated 函数时才有效:

    self.items.valueWillMutate = function () { self.query().postings.valueWillMutate(); }
    self.items.valueHasMutated = function () { self.query().postings.valueHasMutated(); }
    

    这也只有在你使用debug版本的Knockout时才有效,因为在minified版本中,函数名会被压缩。

    【讨论】:

    • 我在使用这种方法传播更新时遇到了问题。有关堆栈跟踪,请参阅帖子。
    • self.query() 可以返回 null 的事实给我带来了一些麻烦,但我想我最终让它工作了。
    • 将此逻辑添加到库中是否有意义?理想情况下,您可以说this.dep = ko.computed(fcn, this, {semantics: 'array'});。除此之外,this.foo = ko.observable(); this.dep = ko.computed(fcn, this); ko.utils.extend(this.dep, ko.observableArray.fn, this.foo);
    • 我不确定这有多少需求。此外,您的扩展示例将无法正常工作,因为从 ko.observable 复制的函数将是错误的。
    • 我想补充一点,我提供的方法不受支持,可能会在更高版本的 Knockout (>2.1) 中中断。
    【解决方案2】:

    你可以让 computed 返回一个 observable:

    self.items = ko.computed(function() { return self.query().postings; });

    然后你可以像这样使用代理observableArray

    self.items().push(...);

    要访问底层数组,您必须解开两个可观察对象:

    var postingsArray = self.items()();

    【讨论】:

    • 我要试一试...但它确实使代码更难理解。能够以声明方式指定计算值的语义会很好。
    猜你喜欢
    • 2012-06-01
    • 2020-06-18
    • 1970-01-01
    • 2014-05-14
    • 2014-04-16
    • 2015-08-31
    • 1970-01-01
    相关资源
    最近更新 更多