删除和取消绑定主干中的子视图

Remove and unbind subviews in Backbone

本文关键字:视图 取消 绑定 删除      更新时间:2023-09-26

我正在使用Backbone 1.1.2,我发现我的应用程序的一些奇怪行为可能是由于zombieviews。我读了Derick Bailey的文章"Run!Zombies!",但后来发现这是为旧版本的Backbone写的(如果我是对的,0.9)。

然后我发现,对于较新的Backbone版本,对视图执行.remove()就足够了,可以正确地杀死它们(因为与ListenTo绑定的事件将通过调用StopListening自动删除)。

在我的应用程序中,我有一个全局视图,在某个时刻会创建两个子视图。当单击重置按钮(在全局视图中)时,应该重新绘制这些视图(但可能首先删除/解除绑定以防止僵尸视频)。

因此,我所做的是将子视图附加到全局视图可以访问的列表中。在初始化功能中:

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. 将子视图添加到全局视图中的列表中是正确的方法吗

感谢您的任何想法!

根据我的经验,简单地调用remove()unbind()/off()就足以防止"僵尸"四处游荡。我要补充的唯一一点是,如果父视图(包含this._views内部子视图的父视图)是从应用程序的另一部分引用的,那么您必须确保通过将这些变量赋值为null来删除这些引用。

在父级中有一个this._views数组来保存其子视图是完全可以的。但是,随着应用程序的发展,您可能需要创建某种子视图管理器和所有其他视图都继承的核心视图。

以下是我过去所做的事情;我希望它能有所帮助:

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

子视图管理器未完成):

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