Javascript 不使用initialize方法定义子视图
我有50多个视图,这些视图都是从一个抽象的基本视图扩展而来的,因此在公共事件处理程序、一些自定义方法和属性等中具有类似的布局和许多其他特性 我目前正在使用基本视图的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:
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
}
});