繁体   English   中英

Backbone.js:如何在模型删除时从事件中取消绑定

[英]Backbone.js: how to unbind from events, on model remove

在骨干网中,我们有一个使用事件聚合器的应用程序,位于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命令。

我看到即使绑定事件方向是这样的方式Object1 - > listen - > Object2它必须被删除,以便Object1丢失任何活动引用。

并且看到听模型remove事件不是解决方案,因为它没有在Collection.reset()调用中调用,那么我们有两个解决方案:

1.覆盖正常的Collection cleanUp

正如@dira sais在这里,您可以覆盖Collection._removeReference以更正确地清除方法。

我不喜欢这个解决方案有两个原因:

  • 我不喜欢覆盖一个必须在它之后调用super的方法。
  • 我不喜欢覆盖私有方法

2.重新包装Collection.reset()调用

相反的是:不添加更深层的功能 ,而是添加更高的功能

然后,不是直接调用Collection.reset() ,而是可以在静默删除之前调用cleanUp模型的实现:

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)

检查jsFiddle

更新:验证在删除绑定事件链接后删除模型

我们首先验证Object1在Object2中监听事件是否会在Obect2 - > Object1的方向上创建一个链接:

我们的目标得以保留

在上面的图像中,我们看到模型(@ 314019)不仅被users集合保留,而且还被观察到的AppEvents对象保留。 看起来像程序员透视链接事件侦听的对象 - >到 - >被侦听的对象但实际上完全相反: 被侦听的对象 - >到 - >正在侦听的对象

现在,如果我们使用Collection.reset()清空Collection,我们看到已删除users链接,但AppEvents链接仍然存在:

我们的目标保留2

users链接已经消失,链接OurModel.collection我认为是Collection._removeReference()作业的一部分。

当我们使用我们的Collection.cleanUp()方法时,对象从内存中消失,我无法让Chrome.profile工具明确告诉我@ 314019对象已被删除,但我可以看到它已不再是内存对象

我认为干净的引用过程是Backbone一个棘手的部分。

Collection删除Model ,集合会注意unbind模型上与其自身绑定的集合的任何事件。 检查这个私有的Collection方法

也许你可以在你的聚合器中使用这样的技术:

// ... Aggregator code
the_model.on( "remove", this.unlinkModel, this );
// ... more Aggregator code

unlinkModel: function( model ){
  model.off( null, null, this );
}

在这种情况下,绑定的方向是Aggregator - > Model 如果方向相反我不认为你必须在模型删除后进行任何清洁。

我没有像fguillen建议的那样用cleanUp包装Collectionreset ,而是直接扩展Collection和覆盖reset 原因是cleanUp仅在客户端代码中生效,但在库(即Backbone )中则不生效。 例如, Collection.fetch可以在内部调用Collection.reset 除非修改Backbone的源代码,否则在调用Collection.fetch之后,我们无法从事件中取消绑定模型(如cleanUp )。

基本上,我建议的代码段如下:

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);
        }
    });

稍后,我们可以基于MyCollection创建新的集合。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM