在没有初始化方法的情况下定义子视图
Defining child views without an initialize method
我有大量视图(超过 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
方法,该方法调用基本 class initialize
(我也扩展了我的events
哈希也很频繁)。我不喜欢这样做,尤其是有这么多扩展基础的视图 class。
在 this post 来自 Backbone Github 存储库问题 Derick Bailey 说:
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.
所以在这个模型上,我应该能够为每个继承视图 class 提供一个 initialize
。这对我来说很有意义;但是我怎样才能实现我继承视图所需的那种总体布局呢?在 constructor
方法中?
我不知道我想要的东西是否可以通过 Marionette 或 LayoutManager 之类的东西开箱即用,我已经简要地看过但从未使用过这两者,但是 我现在更喜欢在香草Backbone中这样做。
在哪里实现基类的初始化class?
我喜欢这样做的方式是在构造函数中初始化基础classes,将initialize
函数留空。它与 Backbone 提供的 initialize
function is only a convenience 一样有意义,实际上只是构造函数的扩展。
事实上,Backbone经常这样做。我们经常覆盖的大多数(如果不是全部)函数和属性只是为了轻松覆盖。
下面是此类示例的快速列表:
- 型号:
initialize
、defaults
、idAttribute
、validate
、urlRoot
、parse
等
- Collection:
initialize
、url
、model
、modelId
、comparator
、parse
等
- 视图:
initialize
、attributes
、el
、template
、render
、events
、className
、id
,等等
这些函数留给用户来实现他自己的行为,并将有用的模式保留在基础 class 中,它们应该保持不变,基础 class 行为应该挂钩到其他功能(如果可能)。
有时,这会变得很困难,比如如果您想在 constructor
中调用 initialize
之前,但在设置元素和其他属性之后做某事。在这种情况下,覆盖 _ensureElement
(line 1223) 可能是一个可能的 钩子。
_ensureElement: function() {
// hook before the element is set correctly
App.BaseView.__super__._ensureElement.apply(this, arguments);
// hook just before the initialize is called.
}
这只是一个示例,几乎总有一种方法可以在基础 class 中获得您想要的内容,而无需覆盖 child 也将覆盖的函数。
简单基础class
如果基本视图用在一个小组件中并且被少数 child 视图覆盖,并且大部分由同一程序员使用,那么以下基本视图就足够了。使用 Underscore's _.defaults
and _.extend
将 child class 属性与基础 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
。
然后是一个简单的child视图:
var ChildView = App.BaseView.extend({
events: {
// additional events
},
initialize: function(options) {
// specific initialization
},
onRender: function() {
// additional rendering
}
});
高级基础class
如果您面临以下情况之一:
- 覆盖
render
有问题,不喜欢onRender
-
events
属性(或任何其他 属性)是 child 或 parent 或两者中的函数
- 使用基的程序员class不知道它的细节
然后可以将 child 属性实现包装到新函数中,Underscore's _.wrap
函数就是这样做的。
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
},
});
因此 child 在保持基本 class 行为的同时看起来完全正常。
var ChildView = App.BaseView.extend({
events: function() {
return {
// additional events
};
},
initialize: function(options) {
// specific initialization
},
render: function() {
// additional rendering
}
});
潜在问题
如果您想完全覆盖基本 class 行为,这可能会成为一个问题,您需要在 child class,它可能会让人感到困惑。
假设您有一个特殊的 child 使用一次需要完全覆盖 render
:
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
}
});
所以这不是非黑即白的,应该评估需要什么,什么会阻碍并选择正确的压倒性技术。
我有大量视图(超过 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
方法,该方法调用基本 class initialize
(我也扩展了我的events
哈希也很频繁)。我不喜欢这样做,尤其是有这么多扩展基础的视图 class。
在 this post 来自 Backbone Github 存储库问题 Derick Bailey 说:
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.
所以在这个模型上,我应该能够为每个继承视图 class 提供一个 initialize
。这对我来说很有意义;但是我怎样才能实现我继承视图所需的那种总体布局呢?在 constructor
方法中?
我不知道我想要的东西是否可以通过 Marionette 或 LayoutManager 之类的东西开箱即用,我已经简要地看过但从未使用过这两者,但是 我现在更喜欢在香草Backbone中这样做。
在哪里实现基类的初始化class?
我喜欢这样做的方式是在构造函数中初始化基础classes,将initialize
函数留空。它与 Backbone 提供的 initialize
function is only a convenience 一样有意义,实际上只是构造函数的扩展。
事实上,Backbone经常这样做。我们经常覆盖的大多数(如果不是全部)函数和属性只是为了轻松覆盖。
下面是此类示例的快速列表:
- 型号:
initialize
、defaults
、idAttribute
、validate
、urlRoot
、parse
等 - Collection:
initialize
、url
、model
、modelId
、comparator
、parse
等 - 视图:
initialize
、attributes
、el
、template
、render
、events
、className
、id
,等等
这些函数留给用户来实现他自己的行为,并将有用的模式保留在基础 class 中,它们应该保持不变,基础 class 行为应该挂钩到其他功能(如果可能)。
有时,这会变得很困难,比如如果您想在 constructor
中调用 initialize
之前,但在设置元素和其他属性之后做某事。在这种情况下,覆盖 _ensureElement
(line 1223) 可能是一个可能的 钩子。
_ensureElement: function() {
// hook before the element is set correctly
App.BaseView.__super__._ensureElement.apply(this, arguments);
// hook just before the initialize is called.
}
这只是一个示例,几乎总有一种方法可以在基础 class 中获得您想要的内容,而无需覆盖 child 也将覆盖的函数。
简单基础class
如果基本视图用在一个小组件中并且被少数 child 视图覆盖,并且大部分由同一程序员使用,那么以下基本视图就足够了。使用 Underscore's _.defaults
and _.extend
将 child class 属性与基础 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
。
然后是一个简单的child视图:
var ChildView = App.BaseView.extend({
events: {
// additional events
},
initialize: function(options) {
// specific initialization
},
onRender: function() {
// additional rendering
}
});
高级基础class
如果您面临以下情况之一:
- 覆盖
render
有问题,不喜欢onRender
-
events
属性(或任何其他 属性)是 child 或 parent 或两者中的函数 - 使用基的程序员class不知道它的细节
然后可以将 child 属性实现包装到新函数中,Underscore's _.wrap
函数就是这样做的。
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
},
});
因此 child 在保持基本 class 行为的同时看起来完全正常。
var ChildView = App.BaseView.extend({
events: function() {
return {
// additional events
};
},
initialize: function(options) {
// specific initialization
},
render: function() {
// additional rendering
}
});
潜在问题
如果您想完全覆盖基本 class 行为,这可能会成为一个问题,您需要在 child class,它可能会让人感到困惑。
假设您有一个特殊的 child 使用一次需要完全覆盖 render
:
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
}
});
所以这不是非黑即白的,应该评估需要什么,什么会阻碍并选择正确的压倒性技术。