ユーザーがアイテムのリストをページングする単一ページのアプリがあります。各アイテムには、アイテムのリストがあります。
監視可能な配列は、AJAX 要求を介して取得されたサーバーからの新しいアイテムで更新されます。これはすべてうまくいきます。
残念なことに、数ページ後、実行される操作の数 (および FireFox や IE8 などのブラウザーで使用されるメモリの量) は増え続けます。監視可能な配列内の項目を新しいデータに置き換えたにもかかわらず、監視可能な配列内の要素が適切にクリーンアップされておらず、実際にはまだメモリ内にあるという事実まで追跡しました。
私が見ている問題を再現する小さな例を作成しました:
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 ViewModel:
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);
フィドルを見ると、「ログ」には、これまでに作成されたすべての ViewModelのエントリが含まれていることがわかります。計算されたプロパティSubItem.computed
は、これらの各アイテムがなくなったと予想した後でも実行されます。これにより、アプリケーションのパフォーマンスが大幅に低下しています。
だから私の質問は:
- ここで何が間違っていますか?実際に手動で破棄する必要がある ViewModel を KnockoutJS が破棄することを期待していますか?
ko.computed
onの使用がSubItem
問題の原因ですか?- KnockoutJS がこれらのビューモデルを破棄しない場合、どのように自分で破棄する必要がありますか?
更新:SubItem
さらに掘り下げた後、計算されたプロパティが原因であると確信しています。ただし、そのプロパティがまだ評価されている理由はまだわかりません。SubItem
観測可能な配列が更新されたときに破棄されるべきではありませんか?