在Backbone中删除和取消绑定子视图

时间:2015-02-04 20:53:16

标签: javascript backbone.js views

我正在使用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

我的问题有两个:

  1. 是否正在调用.remove和.unbind足以阻止zombieviews?
  2. 将子视图添加到全局视图中的列表中是否正确执行此操作?
  3. 任何想法都表示赞赏!

1 个答案:

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