我理解当通过.remove()删除视图时,会在该视图上调用.stopListening()以删除与Backbone中该视图关联的所有事件侦听器。来自Backbone文档:
删除 view.remove()
从DOM中删除视图,并调用stopListening以删除视图已侦听的任何绑定事件 。
我有一些视图附加到一个容器上,该容器只有通过Backbone的事件钩子才有与dom动作相关的事件。
var View = Backbone.View.extend({
events : {
'input keyup' : 'searchDropdown'
},
searchDropdown: function () {
$('dropdown').empty();
//Appends views based on search
}
});
我的问题实际上是在一个容器上调用$ .empty()时是否泄漏任何内存(重要或不重要),该容器有效地删除了其中附加的视图。如果我是,那么在这些视图上访问和调用.remove()有什么好的约定吗?
答案 0 :(得分:2)
You don't need any special framework for this but it's a good idea to implement removal properly and not depend on the browser being smart enough to do this. Sometimes in a large app you will find you specifically need to override the remove method to do some special cleanup - for instance you are using a library in that view which has a destroy
method.
A modern browser tends to have a GC which is smart enough for most cases but I still prefer not to rely on that. Recently I came on to a project in Backbone which had no concept of subviews and I reduced the leaking nodes by 50% by changing to remove
from empty
(in Chrome 43). It's very hard to have a large javascript app not leak memory, my advice is to monitor it early on: If a DOM Element is removed, are its listeners also removed from memory?
Watch out for things which leak a lot of memory - like images. I had some code on a project that did something like this:
var image = new Image();
image.onLoad(.. reference `image` ..)
image.src = ...
Basically a pre-loader. And because we weren't explicitly doing image = null
the GC never kicked in because the callback was referencing the image
variable. On an image heavy site we were leaking 1-2mb with every page transition which was crashing phones. Setting the variable to null in a remove
override fixed this.
Calling remove on subviews is as easy as doing something like this:
remove: function() {
this.removeSubviews();
Backbone.View.prototype.remove.call(this);
},
removeSubviews: function() {
if (!_.isEmpty(this.subViews)) {
_.invoke(this.subViews, 'remove');
this.subViews = [];
}
}
You just need to add your subview instances to an array. For example when you create a subview you could have an option like parentView: this
and add it to the array of the parent. I have done more intricate subview systems in the past but that would work fine. On initialize of the views you could do something like:
var parentView = this.options.parentView;
if (parentView) {
(parentView.subViews = parentView.subViews || []).push(this);
}