ExtJS 4でアプリを構築する際に苦労します。その一部は、initComponent()で何かを構成する場合としない場合の混乱です。
たとえば、Sencha独自の MVCアプリケーションアーキテクチャ docでは、最初にグリッドビューを作成するときに、initComponent()メソッドでインラインストアを定義しました。 (「ビューの定義」セクションを参照)
さらに下に、ストアを別のクラスに分解するとき、定義をinitComponent()の外に移動しました。この事実に注意を引く有用なコメントがありますが、説明はありません。 (モデルとストアの作成セクションを参照)
理由は明らかであると思われますが、私はそれを見逃しています。ポインタはありますか?
ExtJSクラスシステムがどのように機能するかを深く理解していない場合は、次のことをお勧めします。
initComponent()
。ですべての非プリミティブ型を宣言します。
拡張するコンポーネントを複数回作成する場合、構成オプション(initComponent
の外部)として宣言された非プリミティブ構成はすべてのインスタンス間で共有されます。
このため、拡張コンポーネント(通常は拡張グリッド)を複数のタブで作成すると、多くの人が問題を経験しました。
この振る舞いは、以下のsraの回答と このSkirtleのDenの記事 で説明されています。 this SO question を読むこともできます。
最初にコメントに立ちます:
@AlexanderTokarev誤解しないでください。コンポーネントの構成や、インスタンスのはるかに悪い状態や、それらをinitComponent()
に移動することについては話しませんが、それは私のポイントではありません。
今、私はこれについてどう思うか。
initComponent()は、このクラスのインスタンスが作成されるときに必要なものをすべて解決する必要があります。これ以上でもそれ以下でもありません。
クラスを定義するときに負荷を台無しにすることができますが、ExtJSクラスシステムがどのように機能するかを人々が理解していないため、そのほとんどが発生します。これはコンポーネントに関するものであるため、以下ではそれらに焦点を当てます。また、これは単純化された例であり、これまで何度も見たようなエラーのみを表示するはずです。
始めましょう:多くの気の利いたことを行うカスタムパネルがあります。これにより、カスタム構成が必要になります。これをfoo
と呼びます。デフォルトの設定オプションとともにクラス定義に追加して、アクセスできるようにします。
Ext.define('Custom', {
extend: 'Ext.panel.Panel',
alias: 'widget.custpanel',
foo: {
bar: null
},
initComponent: function() {
this.callParent(arguments);
}
});
しかし、テスト後に物事は奇妙になります。構成の値は魔法のように変化するようです。これはJSFiddle作成されたすべてのインスタンスが同じfoo
インスタンスを参照しているということです。しかし、最近私はそれをやった
store: {
fields: [ ... ],
proxy: {
type: 'direct',
directFn: 'Direct.Store.getData'
}
}
店でそれが働いた。では、なぜfoo
が機能しないのでしょうか?
ほとんどの人は、この小さなfoo
オブジェクトと(ExtJS)configの間に違いはありません。どちらもオブジェクト(インスタンス)であるため、基本的に正しいです。しかし違いは、senchaが出荷するすべてのクラスが、どの構成プロパティを期待し、それらを処理するかを完全によく知っていることです。
たとえば、グリッドのストアプロパティは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);
}
});
これがJSFiddle
これで、インスタンスが作成されたときにfoo
構成を処理します。これで、このfoo
の例は単純化され、構成を解決するのが必ずしも簡単になるとは限りません。
結論
クラス定義を常に構成として記述してください!プレーンな設定を除き、参照されるインスタンスを含めることはできません。また、インスタンスが作成されたときにこれらを解決して解決する必要があります。
免責事項
私はこの本当に短い文章ですべてをカバーすると主張しません!
私は通常、クラス構成オプションで可能な限り多くの構成を持つことを推奨します。サブクラスで読みやすく、オーバーライドしやすいからです。それに加えて、将来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();
_
そして:
_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'
});
_
これらのアプローチが大きく異なることに注意してください。最初の例では、オブジェクトプロパティ値、ストア構成、使用時のMyPanelクラス名など、多くのハードコーディングを行っています。クラスは拡張不可能になるため、クラスのアイデアは事実上無効になります。 2番目の例では、異なる構成で何度も再利用できるtemplateを作成しています-基本的に、それはクラスシステム全体の目的です。
ただし、実際の違いはさらに深くなります。最初のケースでは、実行時までクラス構成を効果的に延期しますが、2番目のケースでは、definingクラス構成と非常に明確に異なるフェーズで適用する。実際、2番目のアプローチはJavaScriptにネイティブに欠けているもの、つまりコンパイル時段階を導入していると簡単に言うことができます。また、フレームワークコード自体で悪用される可能性が非常に多くあります。いくつかの例が必要な場合は、最新の4.2ベータ版の_Ext.app.Controller
_および_Ext.app.Application
_をご覧ください。
より実用的な観点からは、2番目のアプローチの方が読みやすく対処しやすいため、より優れています。アイデアを理解すると、このように簡単になるため、そのようなコードをすべて記述していることに気付くでしょう。
このように見てください。古いスタイルのWebアプリケーションを作成し、サーバー側でHTMLなどを生成する場合は、コードにHTMLを混在させないようにしてください。左側のテンプレート、右側のコード。これはinitComponent
のデータをハードコーディングするのと実質的に同じです:ある程度までは機能することを確認してください。その後、スパゲッティのボウルになり、維持と拡張が困難になります。ああ、すべてをテストします!うん.
さて、クラス定義時とは対照的に、実行時にinstanceで何かをする必要があるare回があります-古典的な例は、イベントリスナーを適用するか、コントローラーでcontrol
を呼び出すことです。オブジェクトインスタンスから実際の関数参照を取得する必要があり、initComponent
またはinit
で実行する必要があります。ただし、この問題の緩和に取り組んでいます。これをすべてハードコーディングするためのハード要件はありません。 Observable.on()
はすでに文字列リスナー名をサポートしており、MVCスタッフもまもなくサポートします。
上記のコメントで述べたように、物事を説明する記事やガイドを書く必要があります。 4.2がリリースされるまで、おそらく待つ必要があります。一方、この答えは問題を明らかにするはずです。
私がここにたどり着いたとき、私は同じ質問に対する答えを探していましたが、これらの答えが私を失望させました。これらのどれも質問に答えません:initComponent()またはconstructor?
クラス構成オプションオブジェクトは共有されており、インスタンスごとに初期化/処理する必要があることを知ってうれしいですが、コードはコンストラクタとinitComponent()関数に入ることができます。
私の推測では、Componentクラスのコンストラクターは中間のどこかでinitComponent()を呼び出し、私はそれほど間違っていませんでした。ソースコードを見るだけで、実際は AbstractComponentのコンストラクター です。
したがって、次のようになります。
AbstractComponent/ctor:
- stuffBeforeIC()
- initComponent()
- stuffAfterIC()
コンポーネントを拡張すると、次のようになります。
constructor: function () {
yourStuffBefore();
this.callParent(arguments);
yourStuffAfter();
},
initComponent: function () {
this.callParent();
yourInitComp()
}
これらが呼び出される最終的な順序は次のとおりです。
- yourStuffBefore()
- base's ctor by callParent:
- stuffBeforeIC()
- initComponent:
- base's initComponent by callParent
- yourInitComp()
- stuffAfterIC()
- yourStuffAfter()
そのため、最終的にはすべて、stuffBeforeICとstuffAfterICの間にコードを挿入するかどうかに依存します。これは、拡張するクラスのコンストラクターで検索できます。