Backbone.js 视图继承

41

我有一个名为 Pannel 的视图,它只是一个带有关闭按钮的背景。 我想将该视图扩展为名为 PannelAdvanced 的视图。 如何使用backbone.js实现此操作?

目前所有示例都使用Backbone.View.Extend,但这些示例仅扩展了Backbone.View; 我想扩展我的PannelView


它的工作方式完全相同 - Panel.extend({ ... }) - nrabinowitz
我有一个Panel = Backbone.View.extend(),然后我有advancedPanel = Panel.extend(),但是当我在advancedPanel中的initialize方法中对Panel中的变量进行console log时,它显示为undefined。 - Chapsterj
我可以直接调用Panel.extend()而不先实例化Panel吗? - Chapsterj
3个回答

99

继承视图最简单的方法是按照评论中其他人已经建议的去做:

var Pannel = Backbone.View.extend({
});

var PannelAdvanced = Pannel.extend({
});

但是就像你在评论中所指出的那样,如果Pannel中有一个initialize方法,那么如果你还在PannelAdvanced中有一个initialize方法,就不会调用它。因此,你必须显式地调用Pannel的initialize方法:

var Pannel = Backbone.View.extend({
   initialize: function(options){
      console.log('Pannel initialized');
      this.foo = 'bar';
   }
});

var PannelAdvanced = Pannel.extend({
   initialize: function(options){
      Pannel.prototype.initialize.apply(this, [options])
      console.log('PannelAdvanced initialized');
      console.log(this.foo); // Log: bar
   }
});

如果你有很多从Pannel继承的视图,那么这个方法会有点丑陋,因为你需要记得从所有这些视图中调用Pannel的initialize方法。更糟糕的是,如果现在的Pannel没有initialize方法,但是你将来决定添加它,那么你需要去找所有继承类,并确保它们调用了Pannel的initialize方法。所以这里是另一种定义Pannel的方法,这样你继承的视图就不需要调用Pannel的initialize方法:

var Pannel = function (options) {

    // put all of Panel's initialization code here
    console.log('Pannel initialized');
    this.foo = 'bar';

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

_.extend(Pannel.prototype, Backbone.View.prototype, {

    // put all of Panel's methods here. For example:
    sayHi: function () {
        console.log('hello from Pannel');
    }
});

Pannel.extend = Backbone.View.extend;


// other classes inherit from Panel like this:
var PannelAdvanced = Pannel.extend({

    initialize: function (options) {
        console.log('PannelAdvanced initialized');
        console.log(this.foo);
    }
});

var pannelAdvanced = new PannelAdvanced(); //Log: Pannel initialized, PannelAdvanced initialized, bar
pannelAdvanced.sayHi(); // Log: hello from Pannel

1
这个模式不能处理在子类和父类中都声明事件的情况,因为当你调用var PannelAdvanced = Pannel.extend({events:{...}})时,字典中父类的事件键被子类的覆盖了。你可以在父类中声明一个parentEvents键,在父类构造函数中调用this.delgateEvents(this.parentEvents),但是由于backbone中的this line,它们会再次被子视图删除。 - AJP
2
你其实可以这么做。在 childEvents 键下声明子事件,然后在父级中用 return _.extend({"click .parentClass": "parentFunction"}, this.childEvents) 声明一个 events 函数。 - AJP
使用 _super 不是最佳选择。https://github.com/jashkenas/backbone/pull/787#issuecomment-14327908 - Thiago Festa
我同意。使用原型更好。我更新了我的答案以反映这一点。 - Johnny Oshika

29

这就是我喜欢使用Coffeescript的原因之一。像继承这样的东西更加方便易用。为了附和@JohnnyO的正确答案,我可以在Coffeescript中这么写:

class Panel extends Backbone.View
    initialize: ->
        console.log 'Panel initialized'
        @foo = 'bar'

class PanelAdvanced extends Panel
    initialize: ->
        super
        console.log 'PanelAdvanced initialized'
        console.log @foo

6
我将其翻译成 JavaScript 仅是为了确认我是否正确理解:http://jsfiddle.net/7qssz/super 变为 Panel.prototype.initialize.apply(this, arguments); - NicoGranelli
当传递参数时,我无法使其正常工作。它们不会出现在 this.options 中。 - maletor
@Maletor:你使用的是哪个版本的Backbone?它大约在0.9.9版本左右添加了。 - Brian Genisio
可以了。@NicoGranelli 你的代码不是从Coffeescript进行1:1翻译的。请查看并运行jsbin中的代码。从Coffeescript弹出窗口中选择“转换为Javascript”。 - aMarCruz

8

进一步附带一点:

我喜欢@JohnnyO的方法,但想确认结果视图仍然能够完成它应该做的所有事情。考虑到他的方法,我并不认为会有任何问题,但我想要更加确定。

因此,我花了一分钟时间,将Backbone.js Views test suite调整为@JohnnyO建议的多重继承技术。

您可以在http://jsfiddle.net/dimadima/nPWuG/上运行结果。所有测试都通过了。

我的基本视图和扩展视图:

var RegularView = function (options) {
  // All of this code is common to both a `RegularView` and `SuperView`
  // being constructed.
  this.color = options && (options.color || 'Green');

  // If execution arrives here from the construction of
  // a `SuperView`, `Backbone.View` will call `initialize`
  // that belongs to `SuperView`. This happens because here
  // `this` is `SuperView`, and `Backbone.View`, applied with
  // the current `this` calls `this.initialize.apply(this, arguments)`
  Backbone.View.apply(this, arguments)
};

RegularView.extend = Backbone.View.extend;

_.extend(RegularView.prototype, Backbone.View.prototype, {
  // Called if a `RegularView` is constructed`,
  // Not called if a `SuperView` is constructed.
  initialize: function () {
    console.log('RegularView initialized.');
  },

  say_hi: function() {
    console.log('Regular hi!');
  }

});

var SuperView = RegularView.extend({
  // Called if a `SuperView` is constructed`,
  // Not called if a `RegularView` is constructed.
  initialize: function(options) {
    console.log('SuperView initialized.')
  },

  say_hi: function() {
    console.log('Super hi!');
  }
})

对于测试套件,我从 GitHub 获取了最新的视图测试,并将所有Backbone.View替换为RegularView。之后测试使用RegularViewRegularView.extend()的结果来确保两者都能正常运行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接