Как я должен очищать KnockoutJS ViewModels?
У меня есть одностраничное приложение, где пользователь просматривает списки предметов. Каждый элемент, в свою очередь, имеет список элементов.
Наблюдаемый массив обновляется новыми элементами с сервера, полученными с помощью запроса 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 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);
Если вы посмотрите на скрипку, вы заметите, что «журнал» содержит запись длякаждый ViewModel когда-либо созданный, вычисляемое свойствоSubItem.computed
выполняется даже после того, как я ожидал, что каждый из этих пунктов давно исчезнет. Это вызывает серьезное снижение производительности в моем приложении.
Так что мои вопросы:
Что я здесь не так делаю? Ожидаю ли я, что KnockoutJS избавится от ViewModels, которые мне действительно нужно утилизировать вручную?Мое использованиеko.computed
наSubItem
вызывая проблему?Если KnockoutJS не собирается распоряжаться этими моделями представления, как я должен избавляться от них сам?Обновить: После некоторых копаний я почти уверен, что вычисленное свойство вSubItem
виновник Однако я до сих пор не понимаю, почему это свойство все еще оценивается. не должныSubItem
быть уничтоженным при обновлении наблюдаемого массива?