javascript - 我应该如何清理 KnockoutJS ViewModels?

标签 javascript knockout.js

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

可观察数组使用通过 AJAX 请求从服务器检索到的新项目进行更新。这一切都很好。

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

我创建了一个 small example复制了我看到的问题:

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 View 模型:

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);

If you look at the fiddle ,您会注意到“日志”包含一个条目,用于曾经创建的每个 ViewModel。计算属性 SubItem.computed 即使在我预计这些项目中的每一个都早已消失之后仍然运行。这导致我的应用程序性能严重下降。

所以我的问题是:

  • 我在这里做错了什么?我是否希望 KnockoutJS 处理我实际上需要手动处理的 ViewModel?
  • 是我在 SubItem 上使用 ko.computed 导致了这个问题吗?
  • 如果 KnockoutJS 不打算处理这些 View 模型,我应该如何自己处理它们?

更新:经过进一步挖掘,我很确定 SubItem 中的计算属性是罪魁祸首。但是,我仍然不明白为什么该属性(property)仍在评估中。 SubItem 是否应该在更新可观察数组时被销毁?

最佳答案

JavaScript 垃圾收集器只能在所有对它的引用及其依赖项都被删除后才能处理计算的可观察对象。这是因为 Observable 保留对任何依赖于它们的计算 Observable 的引用(反之亦然)。

一个解决方案是让计算的可观察对象在不再有任何依赖关系时自行处置。这可以使用像这样的辅助函数轻松完成。

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

关于javascript - 我应该如何清理 KnockoutJS ViewModels?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14389351/

相关文章:

javascript - document.write 在 Youtube videobar 的 javascript 中表现异常

javascript - knockout JS : adding forms in foreach bindings

javascript - 为什么一个函数表现得像一个计算函数?

javascript - 根据其选定状态更改 knockout.js optionsText

javascript - 无法拖放由 Javascript 创建的项目

javascript - 如何在对象中添加项目?

javascript - 创建 BitmapData 并从中创建 Sprite 以及可能的性能差异

knockout.js - knockout : valueAccessor vs viewModel on custom binding handlers?

javascript - 在页面加载时隐藏空元素,但之后不使用 knockout.js

php - while() 和 onclick