ホームページ > 記事 > ウェブフロントエンド > Angular での依存関係注入パターンの深い理解 (プレイケース)
この記事では、Angular の依存関係注入モードを詳しく理解し、依存関係注入モードのアプリケーションとゲームプレイのケースを共有します。皆様のお役に立てれば幸いです。
angular チュートリアル"]
図 1 コンポーネント通信モード左側の図は情報を送信するだけです親子コンポーネントを介して、ノード a とノード b は多くのノードを介して通信する必要があります。ノード c が何らかの構成を通じてノード b を制御したい場合、それらの間のノードも追加の属性またはイベントを設定して、対応する情報を透過的に送信する必要があります。右図の依存性注入モードのノード c は、ノード a とノード b が通信するためのサービスを提供できます。ノード a はノード c が提供するサービスと直接通信し、ノード b もノード c が提供するサービスと直接通信します。最後に、通信が簡素化され、中間ノードはコンテンツのこの部分に結合されておらず、上位コンポーネントと下位コンポーネントの間で発生する通信を明確に認識しません。v6 より前) では、注入解析プロセスがマルチレベル モジュール インジェクター、マルチレベル コンポーネント インジェクター、および要素インジェクターに分割されています。新しいバージョン (v9 以降) は 2 レベルのモデルに簡素化されています。最初のクエリ チェーンは、静的 DOM レベルの要素インジェクター、コンポーネント インジェクターなど (総称して要素インジェクターと呼ばれます) であり、もう 1 つのクエリ チェーンはモジュールインジェクターです。解析の順序と解析失敗後のデフォルト値については、公式のコード コメント ドキュメント (provider_flag) でより明確に説明されています。
図 2 2 レベルのインジェクター検索依存関係プロセス (画像ソース )
つまり、コンポーネント/命令とコンポーネント/命令レベルの提供 挿入されたコンテンツは、まずコンポーネント ビュー内の要素内の依存関係をルート要素に至るまで検索します。見つからない場合は、要素が現在配置されているモジュール内で検索し、参照します (モジュールを含む)参照とルーティング遅延読み込み参照) が見つかるまでモジュールの親モジュール ルート モジュールとプラットフォーム モジュール。
ここでのインジェクターには継承があることに注意してください。要素インジェクターは、親要素のインジェクターの検索機能を作成および継承できます。モジュール インジェクターも同様です。継続的な継承の後は、js オブジェクトのプロトタイプ チェーンに似たものになります。
依存関係解決の順序の優先順位を理解すると、適切なレベルでコンテンツを提供できます。これには、モジュール インジェクションと要素インジェクションの 2 つのタイプがあることはすでにわかっています。
モジュール インジェクター: @NgModule のメタデータ属性でプロバイダーを構成でき、v6 以降に提供される @Injectable ステートメントを使用することもできます。provideIn はモジュール名 'root として宣言されます」など。 (実際には、ルート モジュールの上に、Platform と Null という 2 つのインジェクターがあります。ここでは説明しません。)
要素インジェクター: コンポーネントのメタデータ属性で @Component を設定できます。 Providers、viewProviders、またはディレクティブの @Directive メタデータ内のプロバイダー。
さらに、実際には、宣言モジュール インジェクターの使用に加えて、@Injectable デコレーターを次のように宣言することもできます。要素インジェクター。多くの場合、シングルトンを実装するためにルートで提供されるように宣言されます。モジュールやコンポーネントがプロバイダーを直接明示的に宣言することを避けるために、クラス自体を通じてメタデータを統合します。この方法では、クラスにコンポーネント ディレクティブ サービスやそれを挿入する他のクラスがない場合、型宣言にリンクされたコードは存在せず、これはコンパイラによって無視されるため、ツリーをシェイクすることができます。
これを提供する別の方法は、InjectionToken を宣言するときに値を直接与えることです。
これらのメソッドの短縮テンプレートは次のとおりです:
@NgModule({ providers: [ // 模块注入器 ] }) export class MyModule {}
@Component({ providers: [ // 元素注入器 - 组件 ], viewProviders: [ // 元素注入器- 组件视图 ] }) export class MyComponent {}
@Directive({ providers: [ // 元素注入器 - 指令 ] }) export class MyDirective {}
@Injectable({ providedIn: 'root' }) export class MyService {}
export const MY_INJECT_TOKEN = new InjectionToken<myclass>('my-inject-token', { providedIn: 'root', factory: () => { return new MyClass(); } });</myclass>
依存関係の場所を指定するオプションが異なるといくつかの違いが生じ、最終的にはパッケージのサイズと注入できる依存関係に影響します。そして依存関係のライフサイクル。シングルトン (ルート)、サービス分離 (モジュール)、複数の編集ウィンドウ (コンポーネント) など、さまざまなシナリオに適用できるさまざまなソリューションがあります。不適切な情報の共有や冗長なコードのパッケージ化を避けるために、適切な場所を選択する必要があります。
インスタンスインジェクションだけを提供すると、Angular フレームワークの依存関係インジェクションの柔軟性が発揮されません。 Angular は多くの柔軟な注入ツールを提供します。useClass は新しいインスタンスを自動的に作成し、useValue は静的な値を使用し、useExisting は既存のインスタンスを再利用でき、useFactory は指定された deps と指定されたコンストラクター パラメーターを使用して関数を通じて構築されます。これらの組み合わせは非常に多用途です。クラスのトークン トークンを切り取って、準備した別のインスタンスに置き換えることができます。トークンを作成して最初に値またはインスタンスを保存し、後で使用する必要があるときに再度置き換えることもできます。インスタンスのローカル情報は別のオブジェクトまたは属性値にマッピングされます。ここでのゲームプレイについては、次のケースを通じて説明しますので、ここでは説明しません。公式サイトにも参考になる作例が多数掲載されています。
Angular でのインジェクションはコンストラクターでインジェクションすることも、get メソッドを通じて既存のインジェクターを取得することもできます。要素を挿入する。
Angular は、注入時にマークするデコレータの追加をサポートしています。バブリングを制限する
@Self or @Optional @Host? Angular DI デコレータのビジュアル ガイド.」では、親コンポーネントと子コンポーネントの間で異なるデコレータが使用された場合に何が起こるかを非常に鮮明に示しています。ヒットインスタンス間の違い。
#図 3 さまざまなインジェクション デコレータのフィルタリング結果
2.4.1 補足: ホスト ビューと @Hostここでの Host はホストを意味します。@Host デコレータはクエリの範囲をホスト要素内に制限します。ホスト要素とは何ですか?コンポーネント B がコンポーネント A のテンプレートで使用されるコンポーネントの場合、コンポーネント A のインスタンスはコンポーネント B のインスタンスのホスト要素です。コンポーネント テンプレートによって生成されるコンテンツはビューと呼ばれます。同じビューでも、コンポーネントが異なれば異なるビューになる場合があります。コンポーネント A が独自のテンプレート スコープ内でコンポーネント B を使用する場合 (図 4 を参照)、A のテンプレート コンテンツによって形成されるビュー (赤いボックスの部分) がコンポーネント A の埋め込みビューであり、コンポーネント B はこのビュー内にあるため、B については、このビューは B のホスト ビューです。デコレータ @Host は検索範囲をホスト ビューに制限します。見つからない場合はバブルアップされません。
図 4 埋め込みビューとホスト ビュー
以下では、実際のビューを使用してみましょう依存関係注入がどのように機能するか、エラーのトラブルシューティング方法、およびプレイ方法を確認するためのケースです。
DevUI コンポーネント ライブラリのモーダル ウィンドウ コンポーネントはサービス ModalService を提供します。モーダル ボックスがポップアップし、カスタム コンポーネントとして構成できます。ビジネスの学生は、このコンポーネントの使用時に、パッケージがカスタム コンポーネントを見つけられないというエラーを報告することがよくあります。
たとえば、次のエラー レポート:
図 5 ModalService を使用する場合、EditorX を参照するコンポーネントを作成するエラー レポートでは、対応するサービスが見つかりませんProvider
ModalService がカスタム コンポーネントを作成する方法を分析します。 ModalService ソース コード 関数を開きます 52 行目と 95 行目。 componentFactoryResolver
が渡されない場合、ModalService によって挿入された componentFactoryResolver
が使用されることがわかります。ほとんどの場合、企業はルート モジュールに DevUIModule を一度導入しますが、現在のモジュールには ModalModule を導入しません。つまり図6の現状はこんな感じです。図 6 によると、ModalService のインジェクターには EditorXModuleService がありません。
図 6 モジュール サービス提供関係図
インジェクターの継承に従って、4 つの解決策があります。
ModalModule が宣言されている場所に EditorXModule を配置し、インジェクターが EditorXModule によって提供される EditorModuleService を見つけられるようにします - これは最悪の解決策です。loadChildren 自体によって実装された遅延読み込みは、ホームページ モジュールの読み込みを減らすことであり、その結果はサブページ 使用する必要のあるコンテンツは AppModule に配置されますが、大きなリッチ テキスト モジュールは初回ロードで読み込まれるため、FMP (First Meaningful Paint) を悪化させるため採用できません。
EditorXModule を導入し、ModalService を使用するモジュールに ModalService を導入することをお勧めします。推奨できない状況が 1 つだけあります。それは、別のトップレベルのパブリック サービスである ModalService を呼び出すことにより、ロードのために不要なモジュールが依然として上位層に配置されるということです。
ModalService を使用するコンポーネントをトリガーするときは、現在のモジュールの componentFactoryResolver
を挿入し、それを ModalService の open 関数パラメーターに渡します。これを使用することをお勧めします。 EditorXModule の紹介です。
使用するモジュールで、ModalService を手動で提供します。挿入された検索の問題を解決することをお勧めします。
4 つのメソッドは、実際には、ModalService によって使用されるインジェクターの内部チェーン内の EditorXModuleService の問題を解決しています。検索チェーンが 2 つのレベルにあることを確認することで、この問題は解決できます。
: モジュールインジェクターの継承と検索範囲。
3.2 ケース 2: CdkVirtualScrollFor が CdkVirtualScrollViewport を見つけられない#図 7 コードの移動と挿入エラーが見つからない
これは、CdkVirtualScrollFor 命令が CdkVirtualScrollViewport を注入する必要があるためです。ただし、要素注入のインジェクター継承システムは静的な AST 関係の DOM を継承しており、動的なものは不可能です。そのため、次のクエリ動作が発生し、検索が失敗します。
図 8 要素インジェクターのクエリ チェーンの検索範囲
最終解決策: 1) 元のコードの位置を変更しない、または 2) コード全体を変更する必要があります。テンプレートに埋め込まれていることがわかります。
図 9 CdkVitualScrollFo が CdkVirtualScrollViewport を見つけられるようにモジュール全体を埋め込む (解決策 2)
ナレッジ ポイントの概要: 要素の挿入コントローラーのクエリ チェーンは、静的テンプレートの DOM 要素の祖先です。
このケースは、このブログ「Angular: Nested」から引用されています。テンプレート駆動型フォーム》。
フォーム検証を使用するときにも同じ問題が発生しました。図 10 に示すように、いくつかの理由から、3 つのフィールドのアドレスをコンポーネントにカプセル化して再利用します。
図 10 フォームの 3 つのアドレス フィールドをサブコンポーネントにカプセル化する
この時点で、エラーが報告されていることがわかります。 ngModelGroup
ホスト内の ControlContainer
が必要です。これは、ngForm ディレクティブによって提供されるコンテンツです。
図 11 ngModelGroup が ControlContainer を見つけられない
ngModelGroup コードを見ると、ホスト デコレータの制限が追加されているだけであることがわかります。
図 12 ng_model_group.ts は、挿入された ControlContainer の範囲を制限します
ここでは、viewProvider と usingExisting を使用して、AddressComponent のホスト ビューに ControlContainer プロバイダーを追加できます。
図 13 viewProvider を使用して、ネストされたコンポーネントに外部プロバイダーを提供する
ナレッジ ポイントの概要: viewProvider と usingExisting の素晴らしい組み合わせ。
図 14 モジュールの遅延読み込みにより、サービスが同じインスタンス/単一ケースではなくなる
このステートメントにはシングルトンが必要であることは明らかです。シングルトンアプローチ 通常は
providerIn: 'root' で十分ですが、コンポーネント ライブラリの DragDropService をモジュール レベルで提供し、ルート ドメインを直接提供しないのはなぜでしょうか。しかし、よく考えてみると、ここには別の問題もあります。コンポーネント ライブラリ自体は、さまざまな企業が使用するために提供されているため、ページ上の 2 か所に対応する 2 つのドラッグ アンド ドロップ グループがある企業では、リンクを希望しません。このとき、シングルトンはモジュールに基づく自然な分離を破壊します。 そうなると、ビジネス側でシングルトンの置き換えを実装する方が合理的になります。前に説明した依存関係クエリ チェーンを思い出してください。最初に要素インジェクターが検索され、見つからない場合はモジュール インジェクターが開始されます。したがって、代替のアイデアは、要素レベルのプロバイダーを提供できるということです。
図 15 拡張メソッドを使用して新しい DragDropService を取得し、ルート レベルで提供されるものとしてマークを付けます
図 16 同じセレクターを使用して、繰り返しの命令を重ね合わせたり、コンポーネント ライブラリの Draggable 命令と Droppable 命令に追加の命令を重ね合わせたり、DragDropService のトークンをルートにシングルトンを提供した DragDropGlobalService に置き換えたりすることができます
たとえば、図 15 と 16 では、要素インジェクターを使用して命令を重ね合わせ、DragDropService トークンを独自のグローバル シングルトンのインスタンスに置き換えます。現時点では、グローバル シングルトン DragDropService を使用する必要があるため、これら 2 つの追加命令を宣言してエクスポートするモジュールを導入するだけで、コンポーネント ライブラリの Draggable 命令 Droppable 命令が遅延読み込みモジュール間で通信できるようになります。
知識ポイントのまとめ: 要素インジェクターはモジュール インジェクターよりも優先されます。
DevUI コンポーネント ライブラリのテーマは CSS カスタムを使用します属性 (css 変数) は、テーマの切り替えを実現するための root の CSS 変数値を宣言します。 1 つのインターフェイスで異なるテーマのプレビューを同時に表示したい場合は、DOM 要素でローカルに css 変数を再宣言して、ローカル テーマの機能を実現できます。以前テーマ ディザ ジェネレーターを作成していたとき、この方法を使用してテーマをローカルに適用しました。
図 17 部分的なテーマ関数
しかし、CSS 変数値をローカルに適用するだけでは十分ではありません。はデフォルトでボディの末尾にアタッチされます。つまり、そのアタッチ層がローカル変数の外側にあるため、非常に厄介な問題が発生します。ローカル テーマ コンポーネントのドロップダウン ボックスには、外部テーマのスタイルが表示されます。
図 18 ローカル テーマの外部コンポーネントにアタッチされているオーバーレイ ドロップダウン ボックスのテーマが正しくありません
この場合はどうすればよいですか?アタッチメント ポイントをローカル テーマ dom 内に戻す必要があります。
DevUI コンポーネント ライブラリの DatePickerPro コンポーネントのオーバーレイは、Angular CDK のオーバーレイを使用していることが知られています。一連の分析の後、次のようにインジェクションに置き換えました:
1)まず、OverlayContainer を継承し、独自の ElementOverlayContainer を実装します。以下に示します。
図 19 ElementOverlayContainer のカスタマイズと _createContainer ロジックの置き換え
2) 次に、プレビューのコンポーネント側に新しい ElementOverlayContainer を直接提供し、新しいオーバーレイを提供します。新しいオーバーレイが OverlayContainer を使用できることを確認します。本来、Overlay と OverlayContainer はルート上に提供されていますが、ここではこの 2 つをカバーする必要があります。
##図 20 OverlayContainer をカスタム ElementOverlayContainer に置き換え、新しい Overlay を提供しますこの時点で、Web サイトとポップの DOM をプレビューします。上のレイヤーは、component-preview 要素に正常にアタッチされます。 図 21 cdk のオーバーレイ コンテナーが指定された dom にアタッチされ、部分的なテーマのプレビューが成功します。また、内部にはカスタムの OverlayContainerRef もあります。 DevUI コンポーネント ライブラリ 一部のコンポーネントとモーダル ボックス ドロワー ベンチも、それに応じて置き換える必要があります。最後に、ポップアップ レイヤーとその他のポップアップ レイヤーを実現して、ローカル テーマを完全にサポートできます。知識ポイントの要約: 優れた抽象パターンにより、モジュールを置き換え可能にし、エレガントなアスペクト プログラミングを実現できます。
#3.6 ケース 6: CdkOverlay ではスクロール バーに CdkScrollable 命令を追加する必要がありますが、エントリ コンポーネントの最外層には追加できません。これも前の例と同じシーンです。ページ全体はルーティングを通じて読み込まれます。簡単にするために、コンポーネントのホストにスクロール バーを書きました。
図 22 コンテンツ オーバーフロー スクロール バーは、component:host に overflow:auto を書き込みますこのようにして、より困難な問題に遭遇します。はルーター定義によって指定されます。つまり、
はどこにも明示的に呼び出されません。では、cdkScrollable 命令はどうなるでしょうか。追加してみたらどうでしょうか?解決策は、コードの一部をここに隠し、コアコードのみを残すことです。
図 23 インジェクションによってインスタンスを作成し、ライフ サイクルを手動で呼び出す
ここでは、cdkScrollable のインスタンスがインジェクションによって生成され、ライフ サイクルはコンポーネントのライフ サイクル ステージ中に同期的に呼び出されます。
この解決策は正式な方法ではありませんが、問題は解決します。読者が試してみるためのアイデアと探求としてここに残しておきます。
ナレッジポイントの概要: 依存関係注入構成プロバイダーはインスタンスを作成できますが、インスタンスは通常の Service クラスとして扱われ、完全なライフサイクルを持つことができないことに注意してください。
次のブログ投稿を参照してください。「##」 #ターミナルでの Angular アプリケーションのレンダリング>>
#図 24 RendererFactory2 レンダラとその他のコンテンツを置き換えて、Angular をターミナル上で実行できるようにします
作成者は、 RendererFactory2 など Renderer を使用すると、Angular アプリケーションを端末上で実行できます。これが Angular 設計の柔軟性であり、プラットフォームさえも交換可能であり、強力かつ柔軟です。交換の詳細については元の記事を参照してください。ここでは詳しく説明しません。
ナレッジ ポイントの概要: 依存関係の挿入の力は、プロバイダーが独自に構成して、最終的に置換ロジックを実装できることです。
4 概要依存関係の検索プロセスを正しく理解すると、正確な場所でプロバイダーを構成したり (ケース 1 と 2)、他のインスタンスをシングルトンとして置き換えたり (ケース 4 と 5)、さらにはクロスネストすることができます。コンポーネント パッケージの制約が提供されたインスタンスに接続されるか (ケース 3)、提供されたメソッド カーブが命令のインスタンス化を実装するために使用されます (ケース 6)。
ケース 5 は単純な置き換えのように見えますが、置き換え可能なコード構造を作成できるようにするには、注入モードを深く理解し、各関数をより適切かつ合理的に抽象化する必要があります。この場合、依存性注入の効果を最大限に発揮できなくなります。インジェクション モードでは、モジュールがプラグ可能、プラグイン、およびパーツベースになるためのより多くのスペースが提供され、結合が減少し、柔軟性が向上するため、モジュールがよりエレガントかつ調和して連携できるようになります。
依存関係注入機能は強力です。コンポーネントの通信パスを最適化することに加えて、さらに重要なことに、制御の反転を実現し、カプセル化されたコンポーネントをプログラミングのより多くの側面やビジネス固有のロジックの実装に公開することもできます。柔軟になることもできます。
プログラミング関連の知識について詳しくは、
プログラミング ビデオ以上がAngular での依存関係注入パターンの深い理解 (プレイケース)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。