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

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

问题描述

我有大量视图(超过 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;
  },

  //...

});

然而,我发现对于扩展我的基本视图的许多视图,我需要覆盖 initialize 方法,该方法调用基类 initialize(我还扩展了我的events 哈希也很频繁).我不喜欢这样做,尤其是有这么多扩展基类的视图.

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:

我也不喜欢要求扩展类来调用 superinitialize 之类的方法.这个方法太基础了对于从 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.这对我来说很有意义;但是我如何才能实现我的继承视图所需的那种一般布局?在 constructor 方法中?

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 之类的东西开箱即用,我已经简要看过但从未使用过这两者,但是 我更喜欢目前在 vanilla Backbone 中执行此操作.

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.

推荐答案

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

我喜欢这样做的方式是在构造函数中初始化基类,将 initialize 函数留空.这是有道理的,因为 仅初始化 函数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:

  • 模型:initializedefaultsidAttributevalidateurlRoot解析
  • 集合:initializeurlmodelmodelIdcomparator解析
  • 查看:initializeattributeseltemplaterendereventsclassNameid
  • 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.

有时,这会变得很困难,例如,如果您想在 constructor 中调用 initialize 之前,但在设置元素和其他属性之后执行某些操作.在这种情况下,覆盖 _ensureElement () 可能是一个可能的钩子.

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 的 _.defaults_.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
  • events 属性(或任何其他属性)是子级或父级或两者中的函数
  • 使用基类的程序员不知道它的细节
  • 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.

假设您曾经使用过一个特殊的孩子,需要完全覆盖 render:

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天全站免登陆