我正在使用Backbone 1.1.2,我发现我的应用程序的一些奇怪的行为可能是由于zombieviews。我读了"Run! Zombies!" article from Derick Bailey但稍后发现这是为旧版本的Backbone编写的(如果我是正确的话,则为0.9)。
然后我发现对于较新的Backbone版本,在视图上执行.remove()就足以正确杀死它们(因为与ListenTo绑定的事件将通过调用StopListening自动删除)。
在我的应用程序中,我有一个全局视图,在某些时候会创建两个子视图。单击重置按钮(在全局视图中)时,应重新呈现这些视图(但可能首先删除/解除绑定以防止zombieviews)。
所以我所做的就是将子视图附加到全局视图可访问的列表中。在初始化函数中:
this._views = []; // empty list
在渲染子视图时,我将它们添加到列表中
v = new app.gameView();
this._views.push(v);
在重新渲染子视图之前,我调用了一个循环遍历子视图的函数cleanUp,并为每个子视图执行.remove()后跟.unbind():
_.each(this._views, function(view){
this.remove();
this.unbind();
});
this._views = []; // empty the list for next use
我的问题有两个:
任何想法都表示赞赏!
答案 0 :(得分:1)
根据我的经验,只需拨打remove()
和unbind()/off()
就足以阻止" zombies"闲逛。我唯一要补充的是,如果从应用程序的另一部分引用父视图(包含this._views
中的子视图的那个视图),那么你必须确保您只需将这些变量指定为null即可删除这些引用。
在父级内部使用this._views
数组来保存其子视图是完全没问题的。但是,随着应用程序的增长,您可能希望创建某种Subview Manager和所有其他视图的Core View继承自。
这是我过去所做的事情;我希望它有所帮助:
<强> CoreView 强>:
// Probably all views should inherit from CoreView.
define([
'jquery',
'backbone',
'subviews'
], function($, Backbone, Subviews) {
var CoreView = Backbone.View.extend({
$main: $('#main'),
// Create an empty `subviews` property on all views.
constructor: function() {
this.subviews = new Subviews(this);
// Since we're overriding `constructor` here,
// we need to delegate to Backbone
Backbone.View.prototype.constructor.apply(this, arguments);
},
// Views should be attached to the DOM only with the `attach` method to have the right events thrown.
// Attach the view's element only if it's not already in the DOM.
attach: function() {
if (!this.isAttached()) {
this.$main.append(this.el);
this.trigger('dom:attach');
}
return this;
},
isAttached: function() {
return $.contains(document.body, this.el);
},
// Remove each view's subviews and clean up various properties before
// calling Backbone's remove() method.
remove: function() {
if (this.subviews.size()) {
this.subviews.removeAll();
}
// Remove the DOM element (jQuery makes sure to clean up DOM element's data)
Backbone.View.prototype.remove.apply(this, arguments);
// Fire a helpful `dom:detach` event when the view is removed from the DOM.
this.trigger('dom:detach');
this.off();
return this;
}
});
return CoreView;
});
Subview Manager ( not complete ):
// Simple Subview Manager
define([
'jquery',
'backbone'
], function($, Backbone) {
function Subviews(view) {
this.self = view; // this view
this._entries = {}; // its subviews
}
Subviews.prototype = {
constructor: Subviews,
add: function(name, viewInstance) { ... },
has: function(name) { return !!this._entries[name]; },
get: function(name) { return this._entries[name] && this._entries[name]; },
// By default the subview manager tries to replace an element with
// a `data-subview` attribute with the actual subview element.
attach: function(name) {
// In the parent view's template you would have: `<div data-subview="child1"></div>`
var $subViewOutput = this.self.$('[data-subview="'+name+'"]');
if (this._entries[name] && $subViewOutput.length) {
$subViewOutput.replaceWith(this._entries[name].render().el);
}
},
// When removing a subview we also have to remove it from
// this view's `subviews` property.
remove: function(name) {
if (this._entries && this._entries[name]) {
this._entries[name].remove();
// Cleanup
this._entries[name] = null;
this._entries = _.omit(this._entries, name);
}
},
removeAll: function() {
if (this.size()) {
_.each(this._entries, function(view) {
view.remove(); // it will call remove() in CoreView first
});
}
this._entries = {};
this.self = null;
},
size: function() {
return _.size(this._entries);
}
};
return Subviews;
});
普通视图:
define([
'jquery',
'backbone',
'templates',
'views/coreView',
'views/childView'
],
function($, Backbone, templates, CoreView, ChildView) {
var Widget = CoreView.extend({
tagName: 'section',
id: 'widget123',
template: templates.widget123,
initialize: function() {
this.subviews.add('child1', new ChildView());
this.on('dom:attach', function() {
// When the parent is inserted into the DOM also insert its child1
this.subviews.attach('child1');
});
},
render: function() {
this.$el.html(this.template());
return this;
}
});
var instance = new Widget();
instance.render().attach(); // attach() comes from CoreView
});