没有初始化方法定义子视图 [英] Defining child views without an initialize method

查看:209
本文介绍了没有初始化方法定义子视图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a large number of views (more than 50) which all extend from a single abstract base view, and therefore have a similar layout and many other features in common (event handlers, some custom methods and properties, etc).

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

I am presently using the initialize method of my base view to define the layout, which involves a subview, somewhat like the following:

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;
  },

  //...

});

然而,我发现,对于扩展我的基本视图的许多视图,我需要覆盖初始化方法,它调用基类初始化(我还扩展了我的事件经常使用哈希)。我不喜欢这样做,特别是有很多扩展基类的视图。

I find, however, that for many views which extend my base view I need to override the initialize method, which calls the base class initialize (I also extend my events hash quite often as well). I dislike having to do this, especially with so many views which extend the base class.

这篇文章中来自Backbone Github存储库问题的Derick Bailey说:

In this post from a Backbone Github repository issue Derick Bailey says:


我也不是要求扩展类调用super的粉丝
类似初始化的方法。这个方法非常基础,并且
对于从Backbone构造扩展的任何对象都是如此基础。
它永远不应该由基类型实现 - 一个构造为
的类型,其明确意图是永远不会直接实例化,但是
总是从...扩展。

I'm also not a fan of requiring extending classes to call super methods for something like initialize. This method is so basic and so fundamental to any object that extends from a Backbone construct. It should never be implemented by a base type - a type that is built with the explicit intent of never being instantiated directly, but always extended from.

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

So on this model I should be able to have an initialize available for each inheriting view class. This makes perfect sense to me; but how can I then implement the kind of general layout I need for my inheriting views? In the constructor method?

我不知道我想要的东西是否可能是开箱即用的东西,如Marionette或LayoutManager,我已经简要地看了一下但从未使用过,但我更倾向于在香草骨干中做这件事

I don't know if what I want might be possible out-of-the-box with something like Marionette or LayoutManager, both of which I've briefly looked at but never used, but I would much prefer doing this in vanilla Backbone at the moment.

推荐答案

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



我喜欢这样做的方法是在构造函数中初始化基类离开初始化函数为空。它是有道理的 初始化函数只是Backbone提供的一种便利,实际上只是构造函数的一个扩展。

Where to implement the initializing of the base class?

The way I like to do it is to initialize base classes in the constructor leaving the initialize function empty. It makes sense as the initialize function is only a convenience offered by Backbone and is really just an extension of the constructor.

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

In fact, Backbone do this a lot. Most if not all functions and properties that we override often are there only to be easily overriden.

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

Here's a quick list of such example:


  • 型号:初始化默认 idAttribute 验证 urlRoot 解析等。

  • 收藏:初始化 url model modelId 比较器解析等。

  • 查看:初始化属性 el 模板渲染 events className id 等。

  • Model: initialize, defaults, idAttribute, validate, urlRoot, parse, etc.
  • Collection: initialize, url, model, modelId, comparator, parse, etc.
  • View: initialize, attributes, el, template, render, events, className, id, etc.

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

These functions are left to the user to implement his own behaviors and to keep that useful pattern in a base class, they should be kept untouched and the base class behavior should be hooked into other functions if possible.

有时,它可以得到diffi邪教,就像你想在初始化之前做一些事情在构造函数中调用,但在元素和其他属性之后已设定。在这种情况下,覆盖 _ensureElement 第1223行可能是钩子。

Sometimes, it can get difficult, like if you want to do something before initialize is called in the constructor, but after the element and other properties are set. In this case, overriding _ensureElement (line 1223) could be a possible hook.

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

这只是一个例子,几乎总有一种方法可以得到什么您希望在基类中不覆盖子项也将覆盖的函数。

This was just an example, and there are almost always a way to get what you want in the base class without overriding a function that the child will also override.

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

If the base view is used in a small component and overriden by few child views, and mostly used by the same programmer, the following base view could be enough. Use Underscore's _.defaults and _.extend to merge the child class properties with the base class.

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 直接使用 setElement

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

Then a simple child view:

var ChildView = App.BaseView.extend({
    events: {
        // additional events
    },
    initialize: function(options) {
        // specific initialization
    },
    onRender: function() {
        // additional rendering
    }
});






高级基类



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


Advanced base class

If you're facing one of the following situation:


  • 覆盖 render 有问题,不喜欢 onRender

  • 活动属性(或任何其他属性)是子或父或两者中的函数

  • 使用基类的程序员不知道其具体内容

  • overriding render is problematic, don't like onRender
  • the events property (or any other property) is a function in the child or the parent or both
  • the programmer using the base class don't know about its specifics

然后可以将子属性实现包装到新函数中并且 Underscore的 _ .wrap 功能就是这样。

Then it's possible to wrap the child properties implementation into new functions and Underscore's _.wrap function does just that.

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
    },

});

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

So the child looks completely normal while keeping the base class behaviors.

var ChildView = App.BaseView.extend({
    events: function() {
        return {
            // additional events
        };
    },
    initialize: function(options) {
        // specific initialization
    },
    render: function() {
        // additional rendering
    }
});



潜在问题



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

Potential problems

This could become a problem if you want to override the base class behavior completely, you would need to cancel some of the base class behavior manually in the child class, and it could prove to be confusing.

假设您有一个特殊的孩子,一旦需要完全覆盖渲染

Say you have a special child used once that need to override the render completely:

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
    }
});

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

So it's not black and white and one should evaluate what is needed and what is going to be in the way and choose the right overriding technique.

这篇关于没有初始化方法定义子视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆