web-dev-qa-db-ja.com

角度:遅延読み込みモジュールとサービス

私は遅延読み込みを理解するために this チュートリアルに従っていますが、以下は私の推測です。

シナリオ1:サービスは、子モジュールのproviders配列に配置することで提供されます

シナリオ2:forRootアプローチを使用して、子モジュールでサービスが提供されます。

シナリオ1のコンテキストで、

  • 子モジュールが熱心にロードされると、サービスのインスタンスがルートインジェクターに追加されます。
  • 子モジュールが遅延ロードされると、サービスのインスタンスがルートインジェクターに追加され、サービスの新しいインスタンスが子インジェクターに追加されますが、これは通常のユースケースではありません。

コンテキスト2のシナリオでは、

  • 子モジュールが熱心にロードされると、サービスのインスタンスがルートインジェクターに追加されます。

  • 子モジュールが遅延ロードされると、サービスの同じインスタンスがルートと子モジュールの両方で利用可能になります。これは通常の使用例です。

彼らは次のことに言及しています。

最初は

そのため、モジュールを使用する場合でも、モジュールが遅延ロードされない限り、「プライベート」サービスを使用する方法はありません。

最後に、

この構文は元の構文より少し複雑ですが、CreditCardServiceの1つのインスタンスのみがルートモジュールに追加されることを保証します。 CreditCardModuleがロードされると(遅延ロードされる場合でも)、そのサービスの新しいインスタンスは子インジェクターに追加されません。

インスタンスがルートインジェクターでも使用可能になる場合、サービスが「特権化されている」とどのように言いますか?

よくわかりません。誰か明確にしてください。

10
karthikaruna

このスレッドはかなり古いものですが、このスレッドの将来のつまずきについてこのトピックを検索しながら学んだことにお答えします。

遅延読み込みを使用してサービスを民営化するという概念は適切であり、以下の理由によります。

  • モジュールが遅延ロードされると、ルートインジェクターの子である独自のインジェクターコンテキストが作成されます(正確には親インジェクターです)。これらのサービスは、ルートインジェクターの構成時にインスタンス化されなかったため、ルートインジェクターにプッシュされません。
  • Angular Docによると、サービスのスコープを設定する方法の1つは、サービスを独自のモジュールに提供することです(Module-Aを想定)。そして、他のモジュールBがモジュールAをインポートする場合にのみ、そのサービスのプロバイダーが(モジュールAから)取得され、アクセスできるようになります。これは実際には遅延モジュールで機能し、以下の理由で熱心なモジュールでは機能しません。

  • 熱心なモジュールに対して上記のスコープメソッドを実装すると、そのモジュールのサービスのプロバイダーが作成されます(モジュールAを想定)。しかし、その特定のモジュール「A」がルートモジュールにインポートされると(すべての熱心なモジュールがそうであるように)、ルートインジェクターはそのサービスの単一インスタンスを作成し、ルートインジェクターのスコープ内のそのサービスの重複インスタンスを破棄します(ifモジュールAは他の熱心なモジュールにインポートされました)。したがって、すべての熱心なモジュールは、ルートモジュールにインポートされた任意のモジュールのシングルトンサービスにアクセスできます。

  • 繰り返しますが、アプリケーションのロード時に、ルートインジェクターとモジュールは遅延モジュールとそのサービスを認識しません。したがって、遅延サービスは独自のモジュールで民営化されます。ここで、ルートモジュールが遅延サービスにアクセスするには、angular=モジュールのインポート方法に従う必要があります。これは、基本的に「遅延ロードの想定」モジュールをルートにインポートするものです。モジュールはアプリケーションのロード時に実行されるため、遅延ロードの目的を無効にします。
  • ルートインジェクターからレイジーサービスにアクセスしたい場合。以下を使用できます。

    @Injectable({ 
        providedIn: 'root'
    })
    

遅延サービスのデコレータを使用し、アプリケーションのロード時に遅延モジュールをロードせずにルートインジェクタに注入します。

providedIn: rootオブジェクトなしでルートモジュールのレイジーサービスにアクセスできる場合、以下の例はレイジーロードの実際の実装ではありません。次のリンクを使用できます: https://angular.io/guide/providers#limited-provider-scope-by-lazy-loading-modules

14
Danish Aziz

providedIn: 'root'は、Angular 6:以来、サービスを提供する最も簡単で効率的な方法です。

  1. サービスは、モジュールのプロバイダ配列に追加する必要のないシングルトンとしてアプリケーション全体で利用可能になります(Angular <= 5)など)。
  2. サービスが遅延ロードされたモジュール内でのみ使用される場合、そのモジュールで遅延ロードされます
  3. 使用されない場合、ビルドには含まれません(ツリーが揺れます)。

詳細については、 ドキュメント および NgModule FAQs を読むことを検討してください。

ところで:

  1. アプリケーション全体のシングルトンが必要ない場合は、代わりにプロバイダのコンポーネントの配列を使用してください。
  2. 特定のモジュール以外で他の開発者がサービスを使用しないように範囲を制限する場合は、代わりにプロバイダーのNgModuleの配列を使用します。
7
Mick

ここに私がそれを行う方法があります: https://stackblitz.com/edit/angular-lazy-service-module?file=src%2Fapp%2Fapp.component.ts

これは概念実証です。使用するインジェクター(レイジーサービスに依存関係が必要な場合)と、レイジーロードされたサービスのライフサイクルの管理方法(作成するインスタンスの数など)に注意する必要があります。

私のユースケースは、アプリケーションの複数の領域で使用される非常に大きなサービス(Excelへのエクスポート、400 KB以上のgzip圧縮)がありますが、実際に必要になるまでロード/解析したくない-初期ロードが速い! (実際には、数秒後にモジュールをロードする遅延プリロード戦略も使用しました)。

基本的な考え方は、ルートで遅延モジュール(実際には使用しない)として定義するが、手動でロードをトリガーすることです。また、インジェクショントークンを使用して、そのモジュールのサービスを(一度取得したら)解決します。

遅延モジュール

import { NgModule } from '@angular/core';

import { LazyService } from './lazy-service.service';
import { LAZY_SERVICE_TOKEN } from './lazy-service.contract';

@NgModule({
  providers: [{ provide: LAZY_SERVICE_TOKEN, useClass: LazyService }],
})
export class LazyServiceModule {
}

怠zyなサービス

import { Injectable } from '@angular/core';
import { LazyService as LazyServiceInterface } from './lazy-service.contract';

@Injectable()
export class LazyService implements LazyServiceInterface {
  process(msg: string) {
    return `This message is from the lazy service: ${msg}`;
  }
}

アプリモジュール

@NgModule({
  imports: [BrowserModule,
    RouterModule.forRoot([
      // whatever other routes you have
      {
        path: '?$lazy-service', //some name that will not be used
        loadChildren: 'app/lazy-service/lazy-service.module#LazyServiceModule',
      },
    ])],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
export class AppModule { }

コンポーネント内で使用する

constructor(
  private loader: NgModuleFactoryLoader,
  private injector: Injector,
) {
}

async loadServiceAndCall() {
  const factory = await this.loader.load('app/lazy-service/lazy-service.module#LazyServiceModule');
  const moduleRef = factory.create(this.injector);
  const service: LazyService = moduleRef.injector.get(LAZY_SERVICE_TOKEN);
  this.value = service.process('"from app.component.ts"')
}
0
Andrei Tătar

この記事 で説明できます。

とにかく、要するに:

  • すべてのモジュールは、コンパイル段階でマージされます。
  • 熱心にロードされたときAngularコンパイラーはすべてのサービスをrootInjectorに入れて、アプリ全体でサービスを利用できるようにします。
    • 複数のモジュールが同じトークンでサービスを提供する場合、他のモジュールをインポートするモジュールで定義されたプロバイダーが常に勝ちます。
    • 最後にインポートされたモジュールのプロバイダーは、それらをインポートするモジュールを除き、前のモジュールのプロバイダーをオーバーライドします。
  • LazyLoadedの場合、すべてのモジュールはコンパイル時にまだ1つにマージされますが、各モジュールのインジェクターが作成されます。これから、インジェクターの階層が存在し、コンポーネントがインジェクトされたトークンを探す方法は、そのトークンのより近いプロバイダーを探して階層を登っています。
  • forRoot()*これは、モジュールにアプリ全体や他のモジュールの子だけに提供したいサービスがある場合にのみ使用される規則です。
0
Vijay Barot