gpt4 book ai didi

javascript - 在没有初始化方法的情况下定义 subview

转载 作者:行者123 更新时间:2023-11-29 17:53:48 24 4
gpt4 key购买 nike

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

我目前正在使用我的基本 View 的 initialize 方法来定义布局,它涉及一个 subview ,有点像下面这样:

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

//...

});

但是,我发现对于许多扩展我的基本 View 的 View ,我需要重写 initialize 方法,该方法调用基类 initialize(我还扩展了我的events 也经常散列)。我不喜欢这样做,尤其是有这么多扩展基类的 View 。

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.

所以在这个模型上,我应该能够为每个继承的 View 类提供一个 initialize。这对我来说很有意义;但是我怎样才能实现我继承 View 所需的那种总体布局呢?在 constructor 方法中?

我不知道我想要的东西是否可以通过像 Marionette 或 LayoutManager 这样的东西开箱即用,我已经简要地看过但从未使用过这两者,但我更喜欢目前在 vanilla Backbone 中执行此操作

最佳答案

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

我喜欢的做法是在构造函数中初始化基类,将initialize 函数留空。这是有意义的 initialize function is only a convenience由 Backbone 提供,实际上只是构造函数的扩展。

其实Backbone做的很多。我们经常重写的大多数(如果不是全部)函数和属性只是为了方便重写。

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

  • 模型:初始化默认值idAttribute验证urlRoot解析
  • 集合:初始化url模型modelId比较器解析
  • View: initialize, attributes, el, template, render, 事件classNameid

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

有时,这可能会变得困难,比如如果您想在 constructor 中调用 initialize 之前,但在设置元素和其他属性之后做一些事情。在这种情况下,覆盖 _ensureElement ( line 1223 )可能是一个可能的钩子(Hook)。

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

这只是一个示例,几乎总有一种方法可以在基类中获得您想要的内容,而无需覆盖子类也将覆盖的函数。


简单基类

如果基础 View 在一个小组件中使用并且被少数 subview 覆盖,并且大部分由同一程序员使用,那么下面的基础 View 就足够了。使用 Underscore's _.defaults_.extend将子类属性与基类合并。

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相反。

然后是一个简单的 subview :

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

高级基类

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

  • 覆盖render是有问题的,不喜欢onRender
  • events 属性(或任何其他属性)是子级或父级或两者中的函数
  • 使用基类的程序员不知道它的细节

然后可以将子属性实现包装到新函数中并且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 在保持基类行为的同时看起来完全正常。

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

潜在问题

如果您想完全覆盖基类行为,这可能会成为一个问题,您需要在子类中手动取消一些基类行为,这可能会造成混淆。

假设您有一个特殊的 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
}
});

所以这不是非黑即白的,我们应该评估需要什么,什么会阻碍并选择正确的覆盖技术。

关于javascript - 在没有初始化方法的情况下定义 subview ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40982422/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com