web-dev-qa-db-fra.com

Angulaire: modules de chargement paresseux avec services

J'ai suivi ce tutoriel, pour comprendre le chargement paresseux, et ci-dessous mon inférence.

Scénario 1: Les services sont fournis en les plaçant dans le tableau providers d'un module enfant

Scénario 2: Les services sont fournis dans un module enfant en utilisant l'approche forRoot

Avec le scénario 1 en contexte,

  • Si un module enfant est chargé avec impatience, une instance du service est ajoutée à l'injecteur racine.
  • Si un module enfant est chargé paresseusement, une instance du service est ajoutée à l'injecteur racine et une nouvelle instance du service est ajoutée à l'injecteur enfant, ce qui n'est pas le cas d'utilisation habituel.

Avec le scénario 2 en contexte,

  • Si un module enfant est chargé avec impatience, une instance du service est ajoutée à l'injecteur racine.

  • Si un module enfant est chargé paresseusement, la même instance du service est disponible à la fois dans le module racine et dans le module enfant, ce qui est le cas d'utilisation habituel.

Ils ont mentionné ce qui suit.

Au début,

Donc, même lorsque vous utilisez des modules, il n'y a aucun moyen d'avoir un service "privé" à moins que ... le module ne soit chargé paresseusement.

Finalement,

Bien que cette syntaxe soit un peu plus compliquée que l'original, elle nous garantira qu'une seule instance de CreditCardService est ajoutée au module racine. Lorsque le CreditCardModule est chargé (même paresseux), aucune nouvelle instance de ce service ne sera ajoutée à l'injecteur enfant.

Si l'instance va également être disponible dans l'injecteur racine, comment disent-ils que le service est "privatisé"?

Je suis confus. Quelqu'un veuillez clarifier.

10
karthikaruna

Ce fil est assez ancien mais je vais répondre à ce que j'ai appris en recherchant sur ce sujet les futurs trébucheurs sur ce fil.

Le concept de privatisation d'un service avec chargement paresseux est approprié et pour les raisons suivantes:

  • Lorsqu'un module est chargé paresseusement, il crée son propre contexte d'injecteur, qui est l'enfant de l'injecteur racine (c'est l'injecteur parent pour être précis). Les services qu'ils contiennent ne seront pas poussés vers l'injecteur racine car ils n'ont pas été instanciés lors de la configuration de l'injecteur racine.
  • Angular Doc dit que l'une des façons d'étendre votre service est de les fournir à son propre module (supposons Module-A). Et seulement lorsqu'un autre module B importe le module A, il aura le fournisseur de ce service (du module A) et pourra donc y accéder. Cela fonctionne en fait pour les modules paresseux et non pour les modules désireux pour les raisons ci-dessous:

  • Lorsque vous implémentez la méthode d'étendue ci-dessus pour les modules désireux, cela créera un fournisseur pour les services de ce module (supposons que le module A). Mais lorsque ce module particulier 'A' est importé dans le module racine (comme tous les modules désireux devraient l'être), l'injecteur racine créera une seule instance de ce service et rejettera toute instance en double de ce service dans la portée de l'injecteur racine (si module A a été importé dans tout autre module désireux). Ainsi, tous les modules désireux ont accès à un service singleton de n'importe quel module importé dans le module racine.

  • Encore une fois lors du chargement de l'application, l'injecteur racine et le module ne connaissent pas le module paresseux et ses services. Ainsi les services paresseux sont privatisés dans leur propre module. Maintenant, pour que le module racine ait accès au service paresseux, il doit suivre la manière angular d'importer le module. Qui consiste essentiellement à importer le module "censé être chargé paresseusement" dans la racine module au moment du chargement de l'application, et donc de contourner l'objectif du chargement paresseux.
  • Si vous souhaitez toujours avoir accès au service paresseux à partir de l'injecteur racine. Vous pouvez utiliser:

    @Injectable({ 
        providedIn: 'root'
    })
    

décorateur dans le service paresseux et l'injecter dans l'injecteur racine sans charger le module paresseux à la charge de l'application.

L'exemple que vous suiviez n'est pas une véritable implémentation du chargement paresseux si vous avez accès aux services paresseux dans votre module racine, sans le providedIn: root objet. Vous pouvez passer par ce lien: https://angular.io/guide/providers#limiting-provider-scope-by-lazy-loading-modules

14
Danish Aziz

providedIn: 'root' est le moyen le plus simple et le plus efficace de fournir des services depuis Angular 6:

  1. Le service sera disponible dans toute l'application en tant que singleton sans avoir besoin de l'ajouter au tableau des fournisseurs d'un module (comme Angular <= 5).
  2. Si le service n'est utilisé que dans un module chargé paresseusement, il sera chargé paresseusement avec ce module
  3. S'il n'est jamais utilisé, il ne sera pas contenu dans la construction (arborescence secouée).

Pour plus d'informations, lisez les documentation et FAQ NgModule

Btw:

  1. Si vous ne voulez pas un singleton à l'échelle de l'application, utilisez plutôt le tableau du fournisseur d'un composant.
  2. Si vous souhaitez limiter la portée afin qu'aucun autre développeur n'utilise jamais votre service en dehors d'un module particulier, utilisez plutôt le tableau de NgModule du fournisseur.
7
Mick

Voici comment je le fais: https://stackblitz.com/edit/angular-lazy-service-module?file=src%2Fapp%2Fapp.component.ts

Ceci est une preuve de concept. Vous devez faire attention à l'injecteur que vous utilisez (au cas où le service paresseux aurait besoin de certaines dépendances) et à la façon dont vous gérez le cycle de vie du service chargé paresseux (combien d'instances vous créez, etc.).

Mon cas d'utilisation est qu'il existe un assez gros service (exportation vers Excel, plus de 400 Ko gziped) qui est utilisé dans plusieurs domaines de l'application, mais je ne veux pas le charger/analyser jusqu'à ce qu'il soit réellement nécessaire - chargement initial plus rapide! (J'ai en fait également utilisé une stratégie de précharge de retard qui charge les modules après quelques secondes).

L'idée de base est que vous le définissez comme un module paresseux dans une route (que vous n'utilisez pas réellement) mais que vous déclenchez le chargement manuellement. Vous résolvez également le service à partir de ce module (une fois que vous l'avez) en utilisant un jeton d'injection.

module paresseux

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 {
}

service paresseux

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

module d'application

@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 { }

l'utiliser à l'intérieur d'un composant

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

La meilleure explication que je pourrais vous donner est dans cet article .

Bref, en bref:

  • Tous les modules sont fusionnés pendant la phase de compilation.
  • Lorsqu'il est chargé avec impatience Angular met tous les services dans un rootInjector rendant le service disponible pour l'application entière.
    • Si plusieurs modules fournissent un service avec le même jeton, le fournisseur défini dans le module qui importe les autres modules gagne toujours.
    • Le fournisseur du dernier module importé remplace les fournisseurs des modules précédents, à l'exception du module qui les importe.
  • Lorsque lazyLoaded, chaque module est toujours fusionné en un seul lors de la compilation, mais un injecteur pour chaque module est créé. De là, il existe une hiérarchie d'injecteurs et la façon dont un composant recherche le jeton injecté grimpe dans la hiérarchie à la recherche du fournisseur le plus proche pour ce jeton.
  • forRoot () * c'est seulement une convention utilisée lorsque votre module a des services que vous voulez fournir pour l'application entière et d'autres seulement pour les enfants d'un module.
0
0
Vijay Barot