我应该如何清理KnockoutJS ViewModels? [英] How should I be cleaning up KnockoutJS ViewModels?

查看:72
本文介绍了我应该如何清理KnockoutJS ViewModels?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个单页应用,用户可以在其中浏览项目列表。反过来,每个项目都有一个项目列表。

I have a single-page app where the user pages through lists of items. Each item, in turn, has a list of items.

使用通过AJAX请求检索的服务器中的新项目更新可观察数组。这一切都运行正常。

An observable array is updated with new items from the server retrieved via an AJAX request. This all works fine.

不幸的是,在几页之后,执行的操作数量(以及FireFox和IE8等浏览器中使用的内存量)不断增加。我已经跟踪了这个事实,即我的可观察数组中的元素没有被正确清理并且实际上仍然在内存中,即使我用新数据替换了我的可观察数组中的项目。

Unfortunately after a few pages, the number of operations performed (and the amount of memory used in browsers like FireFox and IE8) keeps going up. I've tracked it down to the fact that elements in my observable array are not being cleaned up properly and are actually still in memory, even though I've replaced the items in my observable array with new data.

我创建了一个小例子来复制问题我我看到了:

I've created a small example that replicates the problem I'm seeing:

HTML:

<p data-bind="text: timesComputed"></p>
<button data-bind="click: more">MORE</button>
<ul data-bind="template: { name: 'items-template', foreach: items }">
</ul>

<script id="items-template">
    <li>
        <p data-bind="text: text"></p>
        <ul data-bind="template: { name: 'subitems-template', foreach: subItems }"></ul>
    </li>
</script>

<script id="subitems-template">
    <li>
        <p data-bind="text: text"></p>
    </li>
</script>

JavaScript / KnockoutJS ViewModels:

var subItemIndex = 0;

$("#clear").on("click", function () {
  $("#log").empty();
});

function log(msg) {
  $("#log").text(function (_, current) {
    return current + "\n" + msg;
  });
}
function Item(num, root) {
  var idx = 0;

  this.text = ko.observable("Item " + num);
  this.subItems = ko.observableArray([]);
  this.addSubItem = function () {
    this.subItems.push(new SubItem(++subItemIndex, root));
  }.bind(this);

  this.addSubItem();
  this.addSubItem();
  this.addSubItem();
}

function SubItem(num, root) {
  this.text = ko.observable("SubItem " + num);
  this.computed = ko.computed(function () {
    log("computing for " + this.text());
    return root.text();
  }, this);

  this.computed.subscribe(function () {
    root.timesComputed(root.timesComputed() + 1);
  }, this);
}

function Root() {
  var i = 0;

  this.items = ko.observableArray([]);
  this.addItem = function () {
    this.items.push(new Item(++i, this));
  }.bind(this);

  this.text = ko.observable("More clicked: ");
  this.timesComputed = ko.observable(0);

  this.more = function () {
    this.items.removeAll();
    this.addItem();
    this.addItem();
    this.addItem();    
    this.timesComputed(0);
    this.text("More clicked " + i);
  }.bind(this);

  this.more();
}

var vm = new Root();

ko.applyBindings(vm);

如果你看一下小提琴,你会注意到log包含每个创建的每个ViewModel的条目。计算属性 SubItem.computed 即使在我预期每个项目早已消失之后也会运行。这导致我的应用程序性能严重下降。

If you look at the fiddle, you will notice that the "log" contains an entry for every single ViewModel ever created. the computed property SubItem.computed is run even after I expected each of those items to be long gone. This is causing a serious degradation in performance in my application.

所以我的问题是


  • 我在这里做错了什么?我是否期望KnockoutJS处理我实际需要手动处理的ViewModel?

  • 我在 SubItem 上使用 ko.computed 导致问题?

  • 如果KnockoutJS不打算处理这些视图模型,我应该如何处理它们?

  • What am I doing wrong here? Am I expecting KnockoutJS to dispose of ViewModels that I actually need to be disposing of manually?
  • Is my use of ko.computed on SubItem causing the issue?
  • If KnockoutJS is not going to dispose of these viewmodels, how should I be disposing of them myself?

更新:经过深入挖掘后,我非常确定 SubItem 中的计算属性是罪魁祸首。但是,我仍然不明白为什么仍在评估该属性。当可观察数组更新时,是否应该销毁 SubItem

Update: After some further digging, I'm pretty sure the computed property in SubItem is the culprit. However, I still don't understand why that property is still being evaluated. Shouldn't SubItem be destroyed when the observable array is updated?

推荐答案

一旦所有对它及其依赖项的引用都被删除,JavaScript垃圾收集器只能处理一个计算的observable。这是因为observables保留对任何依赖于它们的计算的observable的引用(反之亦然)。

The JavaScript garbage collector can only dispose a computed observable once all references to it and its dependencies are dropped. That's because observables keep a reference to any computed observables that depend on them (and vice versa).

一个解决方案是让计算的observable在它不再具有时自行处理任何依赖。这可以使用这样的辅助函数轻松完成。

One solution is to make the computed observable dispose itself when it no longer has any dependencies. This can be done easily using a helper function like this.

function autoDisposeComputed(readFunc) {
    var computed = ko.computed({
        read: readFunc,
        deferEvaluation: true,
        disposeWhen: function() {
            return !computed.getSubscriptionsCount();
        }
    });
    return computed;
}

这篇关于我应该如何清理KnockoutJS ViewModels?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆