Jak mam oczyścić KnockoutJS ViewModels?
Mam aplikację na jedną stronę, w której użytkownik przegląda listy elementów. Każdy przedmiot z kolei ma listę przedmiotów.
Tablica obserwowalna jest aktualizowana o nowe elementy z serwera pobranego przez żądanie AJAX. To wszystko działa dobrze.
Niestety po kilku stronach liczba wykonywanych operacji (i ilość pamięci używanej w przeglądarkach takich jak FireFox i IE8) stale rośnie. Sprawdziłem, że elementy w mojej obserwowalnej tablicy nie są prawidłowo czyszczone i są nadal w pamięci, mimo że zastąpiłem elementy w mojej obserwowalnej tablicy nowymi danymi.
Stworzyłemmały przykład replikuje problem, który widzę:
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);
Jeśli spojrzysz na skrzypce, zauważysz, że „dziennik” zawiera wpis dlakażdy stworzony ViewModel. obliczona właściwośćSubItem.computed
jest uruchamiany nawet po tym, jak spodziewałem się, że każdy z tych przedmiotów zniknie. Powoduje to poważną degradację wydajności w mojej aplikacji.
Więc moje pytania są:
Co ja tu robię źle? Czy oczekuję, że KnockoutJS usunie ViewModels, które faktycznie muszę usuwać ręcznie?Czy moje wykorzystanieko.computed
naSubItem
powoduje problem?Jeśli KnockoutJS nie zamierza pozbyć się tych viewmodeli, jak mam się ich pozbyć?Aktualizacja: Po dalszych kopaniach jestem pewien, że obliczona właściwość wSubItem
jest winowajcą. Nadal jednak nie rozumiem, dlaczego ta właściwość jest nadal oceniana. Nie powinienemSubItem
zostać zniszczone, gdy tablica obserwowalna zostanie zaktualizowana?