没有初始化方法定义子视图

时间:2016-12-05 20:06:13

标签: javascript backbone.js view subview

我有大量的视图(超过50个),这些视图都从单个抽象基础视图扩展,因此具有相似的布局和许多其他共同特征(事件处理程序,一些自定义方法和属性等)。

我目前正在使用基本视图的initialize方法来定义布局,其中涉及子视图,如下所示:

App.BaseView = Backbone.View.extend({

  //...

  initialize: function() {
    this.subView = new App.SubView();
  },

  render: function() {
    this.$el.html(this.template(this.model.toJSON()));
    this.subView.$el = this.$('#subview-container');
    this.subView.render();
    return this;
  },

  //...

});
但是,我发现,对于扩展我的基本视图的许多视图,我需要覆盖initialize方法,该方法调用基类initialize(我还扩展了我的events哈希通常也是如此)。我不喜欢这样做,特别是有很多扩展基类的视图。

来自Backbone Github存储库的this post问题Derick Bailey说:

  

我也不是要求扩展类调用super的粉丝   initialize之类的方法。这种方法非常基础   对于从Backbone构造扩展的任何对象来说都是如此基础。   它永远不应该由基类型实现 - 一种构建的类型   明确的意图是永远不要直接实例化,但是   总是从。延伸。

因此,在这个模型上,我应该可以为每个继承视图类提供initialize。这对我来说非常有意义;但是,我怎样才能实现继承视图所需的一般布局?在constructor方法?

我不知道我想要的东西是否可能是开箱即用的东西,如Marionette或LayoutManager,我曾简要介绍但从未使用过,但我更喜欢目前在vanilla Backbone中执行此操作

1 个答案:

答案 0 :(得分:4)

在哪里实现基类的初始化?

我喜欢这样做的方法是在构造函数中初始化基类,使initialize函数为空。它是有道理的,因为Backbone提供的initialize function is only a convenience实际上只是构造函数的扩展。

事实上,Backbone做了很多。大多数(如果不是全部)我们覆盖的功能和属性通常都只能被轻易覆盖。

以下是此类示例的快速列表:

  • 型号:initializedefaultsidAttributevalidateurlRootparse等。
  • 收藏集:initializeurlmodelmodelIdcomparatorparse等。
  • 查看:initializeattributeseltemplaterendereventsclassName,{{1等等。

这些函数留给用户实现自己的行为并将这个有用的模式保存在基类中,它们应保持不变,并且如果可能的话,基类行为应该挂钩到其他函数中。

有时候,它会变得很困难,比如你想在id中调用initialize之前做某事,但是在设置了元素和其他属性之后。在这种情况下,覆盖constructorline 1223可能是一个可能的挂钩。

_ensureElement

这只是一个例子,几乎总有一种方法可以在基类中获得你想要的东西,而不会覆盖孩子也会覆盖的函数。

简单基类

如果基本视图用在一个小组件中并被少数子视图覆盖,并且主要由同一个程序员使用,则以下基本视图就足够了。使用Underscore's _.defaults_.extend将子类属性与基类合并。

_ensureElement: function() {
    // hook before the element is set correctly
    App.BaseView.__super__._ensureElement.apply(this, arguments);
    // hook just before the initialize is called.
}

不要直接设置App.BaseView = Backbone.View.extend({ events: { // default events }, constructor: function(opt) { var proto = App.BaseView.prototype; // extend child class events with the default if not already defined this.events = _.defaults({}, this.events, proto.events); // Base class specifics this.subView = new App.SubView(); // then Backbone's default behavior, which includes calling initialize. Backbone.View.apply(this, arguments); }, render: function() { this.$el.html(this.template(this.model.toJSON())); // don't set `$el` directly, use `setElement` this.subView .setElement(this.$('#subview-container')) .render(); // make it easy for child view to add their custom rendering. this.onRender(); return this; }, onRender: _.noop, }); ,而是使用setElement

然后是一个简单的子视图:

$el

高级基类

如果您遇到下列情况之一:

  • 覆盖var ChildView = App.BaseView.extend({ events: { // additional events }, initialize: function(options) { // specific initialization }, onRender: function() { // additional rendering } }); 有问题,不喜欢render
  • onRender属性(或任何其他属性)是子级或父级或两者中的函数
  • 使用基类的程序员不了解其具体内容

然后可以将子属性实现包装到新函数中,而Underscore's _.wrap函数就是这样。

events

因此,在保持基类行为的同时,孩子看起来完全正常。

App.BaseView = Backbone.View.extend({
    // works with object literal or function returning an object.
    events: function() {
        return { /* base events */ };
    },

    // wrapping function
    _events: function(events, parent) {
        var parentEvents = App.BaseView.prototype.events;
        if (_.isFunction(parentEvents)) parentEvents = parentEvents.call(this);
        if (parent) return parentEvents; // useful if you want the parent events only
        if (_.isFunction(events)) events = events.call(this);
        return _.extend({}, parentEvents, events);
    },

    constructor: function(opt) {
        var proto = App.BaseView.prototype;

        // wrap the child properties into the parent, so they are always available.
        this.events = _.wrap(this.events, this._events);
        this.render = _.wrap(this.render, proto.render);

        // Base class specifics
        this.subView = new App.SubView();

        // then Backbone's default behavior, which includes calling initialize.
        Backbone.View.apply(this, arguments);
    },

    /**
     * render now serves as both a wrapping function and the base render
     */
    render: function(childRender) {
        // base class implementation
        // ....
        // then call the child render
        if (childRender) childRender.call(this);
        return this
    },

});

潜在问题

如果你想完全覆盖基类行为,这可能会成为一个问题,你需要在子类中手动取消一些基类行为,这可能会让人感到困惑。

假设您有一个需要完全覆盖var ChildView = App.BaseView.extend({ events: function() { return { // additional events }; }, initialize: function(options) { // specific initialization }, render: function() { // additional rendering } }); 的特殊孩子:

render

所以它不是黑白的,人们应该评估需要什么以及将会是什么,并选择正确的覆盖技术。