视图模型的内存泄漏 - 摆脱循环依赖的(ViewModel Memory Leak - Gettin

2019-10-18 15:31发布

我生活在一个knockout.js子视图模型observableArray依赖的observable它们的父视图模型。 父母的observable被绑定到一个select下拉,和孩子们需要的时候就知道价值变化。

我已经通过了整个父视图模型,只是observable ,并且还认购母公司内部的observable ,以更新的孩子“手动”。 所有这些工作的结合的目的。

问题是如果我再清除出孩子observableArray以腾出空间给新来的孩子,或者干脆更换新的孩子的孩子,老孩子在记忆棒周围,因为他们仍然通过knockout.js成立了反依存引用。

我开到任何一个不同的设计模式或方式来告诉淘汰赛停止握住依赖。 在另一个例子中,我试图cleanNode(),但似乎并没有清理这些反向相关性。

码:

function FamilyViewModel(name) {
    this.name = name;
    this.children = ko.observableArray();
}

function ChildViewModel(firstName, lastName, selectedFamilyObservable) {
    this.firstName = ko.observable(firstName);
    this.lastName = ko.observable(lastName);
    this.fullName = ko.computed(function() {
        return this.firstName() + " " + this.lastName();
    }, this);
    this.isSelected = ko.computed(function() {
        return selectedFamilyObservable().name == this.lastName();
    }, this);
}

function PageViewModel() {
    this.families = ko.observableArray([
            new FamilyViewModel("Smith"),
            new FamilyViewModel("Jones"),
            new FamilyViewModel("Brown")
            ]);
    this.selectedFamily = ko.observable();
    this.addChild = _.bind(function() {
        this.selectedFamily().children.push(new ChildViewModel("Frank", this.selectedFamily().name, this.selectedFamily));
    }, this);
    this.resetChildren = _.bind(function() {
        this.selectedFamily().children([]);
    }, this);
}

$(function() {
    ko.applyBindings(new PageViewModel());
});

小提琴:

http://jsfiddle.net/cygnl7/xhzkC/1/

Answer 1:

小提琴解决方案参见前面。

你的问题的关键是,你要不仅跟踪当前选定的家庭(的.selectedFamily ),但你也想,如果他们选择要知道每个家庭。 我认为,最简单的办法是订阅selectedFamily它改变之前监视它的价值和它的变化来设定家庭的后isSelected值。 我喜欢这种方法,因为没有必要通过系列阵列循环才能做到这一点。

self.selectedFamily = ko.observable();
self.selectedFamily.subscribe(function (oldValue) {
    if (oldValue) {
        oldValue.isSelected(false);
    }
}, null, 'beforeChange');
self.selectedFamily.subscribe(function (newValue) {
    if (newValue) {
       newValue.isSelected(true);
    }
});

订阅默认功能通过新值的值已经改变。 在这里,您将有机会获得新选择的家庭,你可以将其设置isSelected属性为true。 但是,订阅有两个内置在得到由默认调用的主题 。 首先是“改变”的话题,我们刚才讨论的,另一种是“beforeChange”。 这得到了价值变动之前打来电话,让你执行任何业务逻辑之前的值是即将改变,像一个设置有机会isSelected标志设置为false。

----------

请注意,这接下来的部分是一些考虑我的经历和额外的信息。 以上是适当的解决方案,但对于那些谁想要知道更多,请继续阅读...

----------

另一种方法是使selectedFamily读/写计算,像这样的私人观察到的后盾:

var _selectedFamily = ko.observable();
self.selectedFamily = ko.computed({
    read: _selectedFamily,
    write: function (newValue) {
        var oldValue = _selectedFamily();
        if (oldValue) {
            oldValue.isSelected(false);
        }
        if (newValue) {
           newValue.isSelected(true);
        }
        _selectedFamily(newValue);
    }
});

我怎么有这样的设置是不是防弹但应该在这里给你概念的想法。 这是个不错的办法,而且是一个我会用,如果我有需要,使基于当前及未来价值的决定将决定设置计算的结果的结果去了。 但是,在这种情况下,我没有设定的粉丝isSelected在这里的标志,因为它的结果值无关。 假设有必要建立一个计算,以容纳该确定时设置,我仍然会与订阅解决方案选择,如前面所描述的值的结果逻辑,但在私下里支持_selectedFamily观察到,在关机的机会,有需要设置的私人背部观察到的值,而通过计算去。


另一块,我在代码中解决的问题是按钮上的点击绑定。 我裹着他们在一个with约束力的。 绑定到功能绑定在默认情况下传递到他们当前上下文的对象。 现在,而不必这样做:

self.addChild = function () {
    var selectedFamily = self.selectedFamily();
    // ...code...
};

你可以做:

self.addChild = function (selectedFamily) {
};

我还移动的<span data-bind="visible: isSelected">| Selected!</span> <span data-bind="visible: isSelected">| Selected!</span>结合了孩子们的环境并进入家庭背景,因为我觉得这是什么用意。 这似乎不可思议,你就会有“| isSelected”每个孩子,而不是姓氏,但是旁边的文字,如果按每个孩子想要它,你可以这样做:

<li data-bind="text: fullName"><span data-bind="visible: $parent.isSelected">| Selected!</span></li>

关于KO的最后一个功能foreach结合与具有数据/作为属性的对象绑定。 它可以使你的标记更可读的,真正与访问父链帮助。 比方说,我们也想用家里的isSelected标志来显示由孩子的名字的东西。 前面我们已经使用引用$parent 。 在某些情况下,你有多重嵌套foreach绑定,你可能会发现自己写的东西像<span data-bind="text: $parents[2].name"></span>其给出了关于你的背景资料很少尝试引用。 使用这种方法,你可以代替写:

<div data-bind="foreach: { data: $root.families, as: 'family' }">
    <ul data-bind="foreach: { data: $family.children, as: 'child }>
        <li>
            <span data-bind="text: child.firstName">
            <span data-bind="text: family.name">
        </li>
    </ul>
</div>

通过这种方式,你甚至不会需要一个计算写出来的第一和lastName。 当我想到我将有一个复杂的嵌套上下文结合结构和/或当我想作一个简单的嵌套结构似乎更可读的,我会用这种方法。

http://jsfiddle.net/34ZjB/1/



Answer 2:

检查我的意见,因为我认为他们会帮你一下。

我做了一些修改,以你的小提琴。 我不明白为什么你列入强调,因为它并不需要绑定的事件。 此外,您childViewModel和familyViewModel的都是实际上只是模型。 对家庭IsSelected属性只是一个额外的约束力每次改变selectedFamily时候你不得不走出去,做额外的更新的意思。 最后,如果你想给孩子重设selectedFamily而已,只是删除ko.utils.arrayForEach功能,只是做selectedFamily()儿童([])。 我不知道你为什么会想只重设所选家庭的儿童,但它是很容易做到的。

需要代码 -

 var addChild = function () {
      self.selectedFamily().push(new childModel(self.selectedFamily().name());
 };

http://jsfiddle.net/xhzkC/2/



文章来源: ViewModel Memory Leak - Getting Rid of a Circular Dependency