rom*_*afe 45 extjs extjs4 extjs4.1
我在ExtJS 4中构建应用程序时很困难,其中一部分是关于何时在initComponent()中配置某些内容以及何时不...
例如,在Sencha自己的MVC Application Architecture doc中,首次创建网格视图时,他们在initComponent()方法中定义了内联存储.(参见"定义视图"部分)
再往下,当他们将商店分解为一个单独的类时,他们将定义移到了initComponent()之外.有一个有用的评论引起了对这一事实的关注,但没有解释.(请参阅创建模型和存储部分)
我猜原因应该是显而易见的,但我很想念它.有什么指针吗?
Izh*_*aki 44
如果您对ExtJS类系统的工作方式不是很了解,您可能需要遵循以下步骤:
声明所有非基本类型
initComponent().
如果要扩展的组件不止一次创建,则initComponent所有实例之间将共享声明为配置选项(外部)的任何非原始配置.
因此,当在多个选项卡上创建扩展组件(通常是扩展网格)时,许多人遇到了问题.
这个行为在sra的答案和下面的Skirtle's Den文章中有解释.您可能还想阅读此SO问题.
sra*_*sra 22
首先,我将对我的评论采取立场:
@AlexanderTokarev别误会我的意思.我没有谈论组件的配置,或更糟糕的实例和移动它们initComponent(),这不是我的观点.
现在我对此有何看法.
initComponent()应解决创建此类实例所需的任何内容.不多也不少.
你可以在定义类时搞乱一个负载,其中大部分是因为人们不理解ExtJS类系统的工作方式.由于这是关于组件,以下将集中于那些.它也将是一个简化的例子,它应该只显示我已经看过很多次的错误.
让我们开始吧:我们有一个定制面板,可以做很多漂亮的事情.这就提出了自定义配置的需求,我们称之为foo.我们将它与我们的默认配置选项一起添加到类定义中,以便我们可以访问它:
Ext.define('Custom', {
extend: 'Ext.panel.Panel',
alias: 'widget.custpanel',
foo: {
bar: null
},
initComponent: function() {
this.callParent(arguments);
}
});
Run Code Online (Sandbox Code Playgroud)
但测试后事情变得奇怪了.我们的配置值似乎神奇地改变了.这是一个JSFiddle
发生的事情是所有创建的实例都引用同一个foo实例.但最近我做到了
store: {
fields: [ ... ],
proxy: {
type: 'direct',
directFn: 'Direct.Store.getData'
}
}
Run Code Online (Sandbox Code Playgroud)
与商店,这工作.那为什么不起作用foo?
大多数人都没有看到这个小foo对象和(ExtJS)配置之间有任何区别,它基本上是正确的,因为它们都是对象(实例).但不同之处在于,sencha发布的所有类都非常清楚他们期望的配置属性并处理它们.
例如,网格的商店属性由the解析,StoreManager因此可以是:
storeId 字符串,或在网格初始化期间,这些中的任何一个都会被实际的商店实例解析和覆盖.商店只是一个例子.我想更为人所知的是items数组.这是一个定义时的数组,并且使用MixedCollection为每个实例覆盖它(如果我没有记错的话).
是的,类定义和从它创建的实例之间存在差异.但是我们需要处理任何包含foo如上所述的引用的新属性,这并不复杂.以下是我们需要做的修复foo示例的方法
Ext.define('Custom', {
extend: 'Ext.panel.Panel',
alias: 'widget.custpanel',
foo: {
bar: null
},
initComponent: function() {
this.foo = Ext.apply({}, this.foo);
this.callParent(arguments);
}
});
Run Code Online (Sandbox Code Playgroud)
这是JSFiddle
现在我们在foo创建实例时处理配置.现在,这个foo示例已经过简化,解析配置并不总是那么容易.
结论
始终将您的类定义编写为配置!除了普通配置之外,它们不得包含任何引用的实例,并且必须在创建实例时处理这些实例以解决它们.
放弃
我并没有声称用这个非常简短的写作来涵盖所有内容!
Ale*_*rev 14
我通常主张在类配置选项中尽可能多地配置,因为它读取更好并且更容易在子类中重写.除此之外,很有可能在未来Sencha Cmd将具有优化编译器,因此如果您保持代码声明,它可以从优化中受益.
相比:
Ext.define('MyPanel', {
extend: 'Ext.grid.Panel',
initComponent: function() {
this.callParent();
this.store = new Ext.data.Store({
fields: [ ... ],
proxy: {
type: 'direct',
directFn: Direct.Store.getData
}
});
this.foo = 'bar';
}
});
...
var panel = new MyPanel();
Run Code Online (Sandbox Code Playgroud)
和:
Ext.define('MyPanel', {
extend: 'Ext.grid.Panel',
alias: 'widget.mypanel',
foo: 'bar',
store: {
fields: [ ... ],
proxy: {
type: 'direct',
directFn: 'Direct.Store.getData'
}
}
});
...
var panel = Ext.widget({
xtype: 'mypanel',
foo: 'baz'
});
Run Code Online (Sandbox Code Playgroud)
请注意这些方法是如何非常不同的.在第一个例子中,我们进行了很多硬编码:对象属性值,存储配置,使用时的MyPanel类名; 我们实际上是在扼杀一个阶级的想法,因为它变得难以理解.在第二个例子中,我们要创建一个模板,可以与可能不同的配置多次重复使用-基本上,这是全班系统是关于什么的.
但是,实际差异更深.在第一种情况下,我们有效地将类配置推迟到运行时,而在第二种情况下,我们定义类配置并在非常不同的阶段应用它.实际上,我们可以很容易地说第二种方法引入了JavaScript本身缺乏的东西:编译时间阶段.它为我们提供了大量的可能性,这些可能性在框架代码本身中被利用; 如果你想要一些例子,看一看Ext.app.Controller,并Ext.app.Application在最新的4.2测试版.
从更实际的角度来看,第二种方法更好,因为它更容易阅读和处理.一旦你掌握了这个想法,你会发现自己编写了所有代码,因为这样就更容易了.
以这种方式看待它:如果你要编写旧式Web应用程序,在服务器端生成HTML和东西,你会尝试不让任何HTML与代码混合,是吗?左侧的模板,右侧的代码.这实际上与硬编码数据相同initComponent:确定它可以工作,直到某一点.然后它变成一碗意大利面条,难以维持和延伸.哦,测试一切!呸.
现在,是时候你需要做的事情有一个实例在运行时,而不是一个类定义的时间-一个经典的例子是将事件侦听器,或调用control的控制器.您必须从对象实例中获取实际的函数引用,并且必须在initComponent或中执行此操作init.但是,我们正在努力缓解这个问题 - 应该没有硬性要求对所有这些进行硬编码; Observable.on()很快就会支持字符串监听器名称和MVC的东西.
正如我在上面的评论中所说,我将不得不为文档撰写文章或指南,解释事情.那可能要等到4.2发布; 同时,这个答案应该对这个问题有所启发,希望如此.
小智 12
当我到达这里时,我一直在寻找同一个问题的答案,看到这些答案让我很失望.这些都没有回答这个问题:initComponent()或构造函数?
很高兴知道类配置选项对象是共享的,您需要为每个实例初始化/处理它们,但代码可以进入构造函数以及initComponent()函数.
我的猜测是Component类的构造函数在中间的某个地方调用initComponent()并且我没有错:只需查看源代码,它实际上是AbstractComponent的构造函数.
所以它看起来像这样:
AbstractComponent/ctor:
- stuffBeforeIC()
- initComponent()
- stuffAfterIC()
Run Code Online (Sandbox Code Playgroud)
现在,如果你扩展一个Component,你会得到这样的东西:
constructor: function () {
yourStuffBefore();
this.callParent(arguments);
yourStuffAfter();
},
initComponent: function () {
this.callParent();
yourInitComp()
}
Run Code Online (Sandbox Code Playgroud)
这些调用的最终顺序是:
- yourStuffBefore()
- base's ctor by callParent:
- stuffBeforeIC()
- initComponent:
- base's initComponent by callParent
- yourInitComp()
- stuffAfterIC()
- yourStuffAfter()
Run Code Online (Sandbox Code Playgroud)
所以最后一切都取决于你是否想要/需要在stuffBeforeIC和stuffAfterIC之间注入你的代码,你可以在你要扩展的类的构造函数中查找它们.