在骨干网中我们有一个使用事件聚合器的应用程序,位于window.App.Events
现在,在许多视图中,我们绑定到该聚合器,并且我在视图上手动编写了一个destroy函数,该函数处理来自该事件聚合器的解绑定,然后删除视图。 (而不是直接删除视图)。
现在,某些模型我们也需要这个功能,但我无法弄清楚如何解决它。
某些模型需要绑定到某些事件,但也许我错了,但是如果我们从集合中删除模型,它会保留在内存中,因为这些绑定仍然存在于事件聚合器中。
模型上没有真正的删除功能,就像视图一样。 那我怎么解决这个问题呢?
修改 根据要求,一些代码示例。
App = {
Events: _.extend({}, Backbone.Events)
};
var User = Backbone.Model.extend({
initialize: function(){
_.bindAll(this, 'hide');
App.Events.bind('burglar-enters-the-building', this.hide);
},
hide: function(burglarName){
this.set({'isHidden': true});
console.warn("%s is hiding... because %s entered the house", this.get('name'), burglarName);
}
});
var Users = Backbone.Collection.extend({
model: User
});
var House = Backbone.Model.extend({
initialize: function(){
this.set({'inhabitants': new Users()});
},
evacuate: function(){
this.get('inhabitants').reset();
}
});
$(function(){
var myHouse = new House({});
myHouse.get('inhabitants').reset([{id: 1, name: 'John'}, {id: 1, name: 'Jane'}]);
console.log('currently living in the house: ', myHouse.get('inhabitants').toJSON());
App.Events.trigger('burglar-enters-the-building', 'burglar1');
myHouse.evacuate();
console.log('currently living in the house: ', myHouse.get('inhabitants').toJSON());
App.Events.trigger('burglar-enters-the-building', 'burglar2');
});
在jsFiddle上查看此代码(控制台中的输出):http://jsfiddle.net/saelfaer/szvFY/1/
正如您所看到的,我不会绑定模型上的事件,而是绑定到事件聚合器。 来自模型本身的解除绑定事件是没有必要的,因为如果它被移除,没有人会再次触发它上面的事件。但eventAggregator始终处于适当的位置,以便于在整个应用程序中传递事件。
代码示例显示,即使将它们从集合中移除,它们也不再存在于房屋内,但是当窃贼进入房屋时仍然执行hide命令。
答案 0 :(得分:13)
我看到即使绑定事件方向是这样的 Object1 - >听 - >必须删除Object2 ,以便 Object1 丢失任何活动引用。
看到听模型remove
事件不是解决方案,因为它没有在Collection.reset()
调用中调用,那么我们有两个解决方案:
作为@dira sais here,您可以覆盖Collection._removeReference
以更正确地清除方法。
我不喜欢这种解决方案有两个原因:
super
的方法。Collection.reset()
次来电相反:不添加更深层次的功能,而是添加上层功能。
然后,您可以直接调用 cleanUp 模型的实现,而不是直接调用Collection.reset()
:
cleanUp: function( data ){
this.each( function( model ) { model.unlink(); } );
this.reset( data );
}
您的代码的分类器版本可能如下所示:
AppEvents = {};
_.extend(AppEvents, Backbone.Events)
var User = Backbone.Model.extend({
initialize: function(){
AppEvents.on('my_event', this.listen, this);
},
listen: function(){
console.log("%s still listening...", this.get('name'));
},
unlink: function(){
AppEvents.off( null, null, this );
}
});
var Users = Backbone.Collection.extend({
model: User,
cleanUp: function( data ){
this.each( function( model ) { model.unlink(); } );
this.reset( data );
}
});
// testing
var users = new Users([{name: 'John'}]);
console.log('users.size: ', users.size()); // 1
AppEvents.trigger('my_event'); // John still listening...
users.cleanUp();
console.log('users.size: ', users.size()); // 0
AppEvents.trigger('my_event'); // (nothing)
检查the jsFiddle。
我们首先验证Object1在Object2中监听事件是否在 Obect2 - >的方向上创建了一个链接。 Object1 :
在上面的图像中,我们看到模型(@ 314019)不仅由users
集合保留,而且还由正在观察的AppEvents
对象保留。对于程序员角度来说,事件链接看起来像侦听的对象 - >到 - >被侦听的对象但实际上完全相反:被监听的对象 - >到 - >正在聆听的对象。
现在,如果我们使用Collection.reset()
清空收藏集,我们会看到users
链接已被删除,但AppEvents
链接仍然存在:
users
链接已消失,链接OurModel.collection
我认为是Collection._removeReference()
工作的一部分。
当我们使用Collection.cleanUp()
方法时,对象会从内存中消失,我无法让Chrome.profile
工具明确告诉我对象@ 314019已被删除但我可以看到它不再是记忆对象。
答案 1 :(得分:1)
我认为干净的引用过程是Backbone
的一个棘手的部分。
当您从Model
中移除Collection
时,收集会关注模型上的unbind
任何与其自身绑定的集合的事件。 Check this private Collection method
也许你可以在你的聚合器中使用这样的技术:
// ... Aggregator code
the_model.on( "remove", this.unlinkModel, this );
// ... more Aggregator code
unlinkModel: function( model ){
model.off( null, null, this );
}
在这种情况下,绑定的方向是聚合器 - >模型。如果方向相反,我不认为你必须在模型移除后进行任何清洁。
答案 2 :(得分:1)
我没有将Collection
的{{1}}与reset
包装为fguillen建议,而是直接扩展cleanUp
并覆盖Collection
。原因是
reset
仅在客户端代码中生效,但不在库(即 Backbone )中生效。
例如,cleanUp
可以在内部调用Collection.fetch
。除非修改 Backbone的源代码,否则我们无法在调用Collection.reset
后解除事件中的模型(如cleanUp
)。
基本上,我建议的代码段如下:
Collection.fetch
稍后,我们可以根据var MyCollection = Backbone.Collection.extend({
reset: function(models, options) {
this.each(function(model) {
model.unlink(); // same as fguillen's code
});
Backbone.Collection.prototype.reset.apply(this, arguments);
}
});
创建新的收藏。