【问题标题】:Backbone.js view inheritanceBackbone.js 视图继承
【发布时间】:2011-10-12 04:02:56
【问题描述】:

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

现在所有的例子都有Backbone.View.Extend,但那些只是扩展Backbone.View;我想扩展我的PannelView

【问题讨论】:

  • 它的工作方式完全相同 - Panel.extend({ ... })
  • 我有我的 Panel = Backbone.View.extend() 然后我有 advancedPanel = Panel.extend() 但是当我在 AdvancedPanel 内部的 Panel 的初始化方法中为变量执行控制台日志时显示为未定义。
  • 我可以直接调用 Panel.extend() 而不先实例化 Panel。

标签: inheritance backbone.js


【解决方案1】:

继承视图的最简单方法是按照其他人已经在 cmets 中提出的建议:

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

var PannelAdvanced = Pannel.extend({
});

但是就像您在 cmets 中指出的那样,如果您在 Pannel 中有一个初始化方法,那么如果您在 PannelAdvanced 中也有一个初始化方法,它将不会被调用,因此您必须显式调用 Pannel 的初始化方法:

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 的初始化。更糟糕的是,如果 Pannel 现在没有 initialize 方法,但您选择在将来添加它,那么您将来需要转到所有继承的类并确保它们调用 Pannel 的初始化。所以这里有另一种定义 Pannel 的方法,这样你继承的视图就不需要调用 Pannel 的初始化方法:

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

【讨论】:

  • 此模式不适用于在子级和父级中声明的事件,因为当您调用 var PannelAdvanced = Pannel.extend({events:{...}}) 时,dict 中父级的事件键被子级覆盖。您可以在父级中声明一个 parentsEvents 键,然后在父级构造函数中调用this.delgateEvents(this.parentEvents),但由于主干中的this line,它们将再次由于子视图而被删除。
  • 其实你可以做到的。在 childEvents 键或其他内容中声明子事件,然后在父项中声明 events 函数:return _.extend({"click .parentClass": "parentFunction"}, this.childEvents)
  • 我同意。最好使用原型。我更新了我的答案以反映这一点。
【解决方案2】:

这是我如此喜欢使用 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

【讨论】:

  • 我把它翻译成javascript只是为了看看我能不能把它写出来:jsfiddle.net/7qsszsuper变成Panel.prototype.initialize.apply(this, arguments);
  • 我在传递参数时无法让它工作。他们没有出现在this.options
  • @Maletor:你使用的是什么版本的 Backbone?它是在 0.9.9 版本或类似版本中添加的。
  • 有效。 @NicoGranelli 您的代码不是来自 Coffeescript 的 1:1 翻译。请see and run the code in jsbin。从 CoffeeScript 弹出窗口中选择 Convert to Javascript
【解决方案3】:

再进一步捎带一下:

我喜欢@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!');
  }
})

对于测试套件,我使用了latest views tests from GitHub 并将出现的Backbone.View 替换为RegularView。然后测试使用RegularViewRegularView.extend() 的结果来确保两者都做他们应该做的事情。

【讨论】:

    猜你喜欢
    • 2014-10-04
    • 1970-01-01
    • 1970-01-01
    • 2017-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-27
    相关资源
    最近更新 更多