Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/backbone.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 不使用initialize方法定义子视图_Javascript_Backbone.js_View_Subview - Fatal编程技术网

Javascript 不使用initialize方法定义子视图

Javascript 不使用initialize方法定义子视图,javascript,backbone.js,view,subview,Javascript,Backbone.js,View,Subview,我有50多个视图,这些视图都是从一个抽象的基本视图扩展而来的,因此在公共事件处理程序、一些自定义方法和属性等中具有类似的布局和许多其他特性 我目前正在使用基本视图的initialize方法来定义布局,其中包含一个子视图,有点类似于以下内容: App.BaseView = Backbone.View.extend({ //... initialize: function() { this.subView = new App.SubView(); }, render:

我有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,我还经常扩展我的事件哈希。我不喜欢这样做,尤其是有这么多扩展基类的视图

在Github主干存储库问题中,Derick Bailey说:

我也不喜欢要求扩展类调用super 初始化之类的方法。这种方法是如此的基本和简单 所以对于任何从主干结构延伸出来的对象来说都是基本的。 它永远不应该由基类型实现——一种构建的类型 明确的意图是永远不会被直接实例化,但是 总是从开始延伸

所以在这个模型上,我应该能够为每个继承视图类提供一个初始化。这对我来说很有意义;但是如何实现继承视图所需的总体布局呢?在构造函数方法中

我不知道我想要的东西是否可以用木偶或LayoutManager这样的东西开箱即用,这两种东西我都简要地看了一下,但从来没有用过,但我现在更喜欢在vanilla主干中这样做。

在哪里实现基类的初始化? 我喜欢这样做的方式是在构造函数中初始化基类,使initialize函数为空。它作为主干提供的方法是有意义的,实际上只是构造函数的一个扩展

事实上,主干网经常这样做。我们通常覆盖的大多数函数和属性(如果不是所有的话)都只是为了容易地被覆盖

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

模型:初始化、默认、idAttribute、验证、urlRoot、解析等。 集合:初始化、url、模型、modelId、比较器、解析等。 视图:初始化、属性、el、模板、渲染、事件、类名、id等。 这些函数留给用户来实现自己的行为,为了在基类中保持有用的模式,它们应该保持不变,如果可能的话,基类行为应该连接到其他函数中

有时,这可能会变得很困难,比如如果您想在构造函数中调用initialize之前,但在设置元素和其他属性之后执行某些操作。在这种情况下,最重要的保证可能是一个钩子

这只是一个示例,几乎总是有一种方法可以在基类中获得所需的内容,而不必重写子类也将重写的函数

简单基类 如果在一个小组件中使用基本视图,并由几个子视图覆盖,并且主要由同一个程序员使用,那么下面的基本视图就足够了。使用和将子类属性与基类合并

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,

});
不要直接设置$el,而是使用

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

var ChildView = App.BaseView.extend({
    events: {
        // additional events
    },
    initialize: function(options) {
        // specific initialization
    },
    onRender: function() {
        // additional rendering
    }
});
高级基类 如果您面临以下情况之一:

覆盖渲染有问题,不喜欢onRender 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
    }
});
潜在问题 如果您想要完全覆盖基类行为,那么这可能会成为一个问题,您需要在子类中手动取消一些基类行为,这可能会导致混淆

假设您使用了一个特殊的子对象,需要完全覆盖渲染:

var SpecialChildView = App.BaseView.extend({
    initialize: function(options) {
        // Cancel the base class wrapping by putting 
        // the this class's prototype render back.
        this.render = SpecialChildView.prototype.render;

        // specific initialization
    },
    render: function() {
        // new rendering
    }
});

因此,这不是黑白分明的,我们应该评估需要什么以及会有什么阻碍,并选择正确的覆盖技术。

我在回答中添加了另一个例子。如果您有问题或希望我为您添加更多信息,请随时提问。非常感谢您全面、快速的回答。我非常感谢你抽出时间来做这一切。事实上,这看起来是一个非常好的解决方案。我之前对接触构造器相当谨慎,认为它是“禁区”,但这还为时过早。事实上,在查看其CollectionView的木偶图源代码时,它也重新实现了构造函数。我应该先看看这个。再次非常感谢,这帮助我以不同的方式思考问题。我已经接受了你的回答。
var SpecialChildView = App.BaseView.extend({
    initialize: function(options) {
        // Cancel the base class wrapping by putting 
        // the this class's prototype render back.
        this.render = SpecialChildView.prototype.render;

        // specific initialization
    },
    render: function() {
        // new rendering
    }
});