【问题标题】:Backbone view inheritance主干视图继承
【发布时间】:2017-10-04 10:34:08
【问题描述】:

我知道有人会将此标记为重复,但我浏览了几乎所有与此主题相关的帖子,这不是我想要的。所以这是自上周以来一直困扰我的事情。

我的任务是创建视图的原子设计。将有一个核心基础视图,然后另一个视图将扩展它,依此类推。 element->panel->window, element->panel->popup 等。使用 Backbone.View.extend 我可以简单地做到这一点

var BaseView = Backbone.View.extend({
  initialize : function(options) {
      this.$el.attr('cid', this.cid);
      this.base = options.base || arguments[0]['base'];
      this.parent = options.parent || arguments[0]['parent'];

      this.children = [];

      if(typeof this.template !== 'undefined') {
          if (typeof this.template=='function') {
              // do nothing. template is already a underscore template and will be parsed when first call;
          }
          else if (typeof this.template=='string' && this.template.substr(0,1)=='#') {
              if ($(this.template).length >0  ) {
                  this.template = _.template($(this.template).html());
              }
              else {
                  console.warn('Template element ' + this.template + 'could not be located in DOM.');
              }
          }
          else {
              this.template = _.template(this.template);
          }
      }
      else {
          this.template = _.template('<span></span>');
      }
      if (typeof this.parent!=='undefined' && this.parent ) {
          this.parent.add.apply(this.parent, [this]);
      }
  },
  add: function(child){
    this.children.push(child);
  },
  getChildren : function(){
    return this.children;
  },
  clean: function(){
    this.$el.empty();
  },

  close: function () {
      BaseView.prototype.clear.apply(this, [true]);
      this.undelegateEvents();
      this.unbind();
      this.stopListening();
      this.remove();
  },
  clear: function(){    
      if (this.children.length > 0) {
          empty = empty || false;
          _.each(this.getChildren(), function (child, index) {
              Baseview.prototype.close.apply(child);
          });
          this.children = [];
          if (empty) {
              this.$el.empty();
          }
      }
      return this;
  }
})

如果我尝试将其用作

var Layout = new BaseView.extend({
    el: '#someElement',
    template : '#sometemplate',
    initialize : function(){
      this.childView = new ChildView({parent: this, base: this, collection: someCollection});
      return this;
    },
    render: function(){
        this.clean().$el.append(this.template({}));
        this.$('.content').append(this.childView.render().$el);
        return this;
    },
});

var ChildView = BaseView.extend({
  tagName : 'div',
  template : '#childTemplate',
  initialize : function(){
    return this;
  },
  render: function(){
    var self = this;
    this.clean().$el.append(this.template({}));
    this.$list = this.$('ul');
    _.each( this.collection.models, function(model){     
        var grandChildView = new GrandChildView({parent: self, base: self.base, model: model});
        self.$list.append(grandChildView.render().$el);
    })
    return this;
  }
});


var GrandChildView = BaseView.extend({
  tagName : 'li',
  template : '#grandChildTemplate',
  initialize : function(){
    return this;
  },
  render: function(){
     this.clean().$el(this.template(this.model.toJSON()));
     return this;
  }
});
$(function(){
  new Layout();
})

不起作用,因为不是在 BaseView 上运行初始化,而是 Backbone 调用首先启动,并且 this.template 和所有其他都未定义。

然后我尝试将其替换为constructor,而不是在 BaseView 上进行初始化。但后来我最终出现this.$el undefined 错误,因为尚未调用 Backbone.View.constructor 所以还没有 this.$el _ensureElement 创建的 _ensureElement

所以通过一些研究,我发现唯一的事情就是使用

Backbone.View.prototype.constructor.apply(this,[options]);

但这也会导致类似的问题,在 Backbone.View 结束时,调用 this.initialize.apply(this, [options]),然后改为初始化子对象。所以我被困住了,无法解决这个问题。

我也知道我可以从子视图中调用父级的初始化函数,但这并不可取,因为有很多子视图相互扩展。这就是我传递父对象以将后面的对象附加到它的子对象的原因。

我想要完成的是创建一个包装扩展对象,稍后我可以为另一个对象扩展它,但同时它应该在原始基础视图上运行一些常见任务,附加额外的原型方法,然后调用调用者初始化。

太假了

var BaseView {
   extend Backbone view with the passed arguments, 
   check for base, parent arguments and set them
   check for template option, if exists then get the element, create template function and replace with the string one,
   if there is parent view passed, then attach yourself to children of parent
   call your own initialize method,
   return 
}

【问题讨论】:

  • 这是make a good base class with Backbone 的不同方式。看起来很多职责没有在正确的类中处理。我从来不需要将父类传递给它的子类,由父类处理子类并监听事件总是更好的,这样子类可以保持专注和可重用。
  • 谢谢。是的,我同意你提到的亲子关系。但具有原子设计理念。子继承可能有额外的东西。想想像旧的 ExtJs 之类的东西。原始基类是 Ext.Element,它创建一个空的 div 元素、id 等属性。然后 Ext.Panel 扩展 Ext.Element,并使用 Ext.Elements 功能,添加面板标题、正文、设计等,然后 Ext.Window 使用几乎相同的功能但浮动等扩展 Ext.Panel。它们都有初始化方法并被调用为了到达主要元素。
  • 但是在使用调用方配置初始化 Ext.Window 之前,首先创建 Ext.Element。我想要完成的是类似的。 BaseView 应该是一个处理大部分预初始化功能的包装器内容设置,然后在从中创建新对象时,它会将自身附加到父对象,因此当父对象被销毁时,所有子对象都可以使用它。否则,当父级被移除时,子级将成为孤儿。

标签: javascript backbone.js


【解决方案1】:

如果我理解正确,您想在实例化子视图时运行“父视图”的初始化方法吗?如果正确,请参阅此帖子:https://stackoverflow.com/a/8596882/1819684

您缺少的是该帖子中引用的主干文档中有关“超级”的注释。

根据您的评论,我认为这就是您要查找的内容。您必须在主干中显式调用“超级”方法(父类的方法),如我引用的帖子中所示。您可以在调用父方法之前和之后在“初始化”中做任何您想做/需要做的事情。我还发现了这个:Defining child views without an initialize method 这可能会有所帮助。

var BaseView = Backbone.View.extend({
  el: '#container1',
  initialize: function(){
    console.log('base view init');
    this.render();
  },
  render: function(){
    this.$el.html("Hello World 1");
  }
});

var ChildView = BaseView.extend({
  el: '#container2',
  initialize: function() {
    console.log('before call to parent (BaseView) init');
    BaseView.prototype.initialize.apply(this, arguments);
    console.log('ChildView init');
  },
  render: function(){
    this.$el.html("Hello World 2");
  }
});

var GrandChildView = ChildView.extend({
  el: '#container3',
  initialize: function() {
    console.log('before call to parent (ChildView) init');
    ChildView.prototype.initialize.apply(this, arguments);
    console.log('GrandChildView init');
  },
  render: function(){
    this.$el.html("Hello World 3");
  }
});


var appView = new BaseView();
var appView = new ChildView();
var appView = new GrandChildView();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone.js"></script>

<div id="container1">Loading...</div>
<div id="container2">Loading...</div>
<div id="container3">Loading...</div>

【讨论】:

  • 是和不是。我想先调用父级的初始化方法,准备一些属性(模板等),然后继续子级的初始化。因为孩子也可以扩展。这样它将覆盖它imo
  • 我在评论中添加了您所说的示例。我想这就是你想要的。
  • 谢谢你的例子。我看到你做了什么。但正如我在我的问题中提到的,我不想,也不能在子构造函数中调用父原型,因为它们可以在不同的容器中或从不同类型的类扩展。这就是我有 parent 和 base 参数的主要原因,因此,parent 负责在删除时处理孩子。否则,当我从容器中删除所有孩子时,我将不得不手动处理垃圾收集,并且在我所在的开发团队中,它可能会被遗忘并且很容易导致内存泄漏。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多