Angular: Ленивые загрузочные модули с сервисами

Я следовал этому уроку, чтобы понять ленивую загрузку, и ниже мой вывод.

Сценарий 1: Услуги предоставляются путем помещения их в providers массив дочернего модуля

Сценарий 2. Услуги предоставляются в дочернем модуле с использованием forRoot подход

Со сценарием 1 в контексте,

  • Если дочерний модуль загружен с нетерпением, экземпляр службы добавляется в корневой инжектор.
  • Если дочерний модуль загружается лениво, экземпляр службы добавляется в корневой инжектор, а новый экземпляр службы добавляется в дочерний инжектор, что не является обычным вариантом использования.

Со сценарием 2 в контексте,

  • Если дочерний модуль загружен с нетерпением, экземпляр службы добавляется в корневой инжектор.

  • Если дочерний модуль загружается лениво, один и тот же экземпляр службы доступен как в корневом, так и в дочернем модулях, что является обычным вариантом использования.

Они упомянули следующее.

В начале,

Таким образом, даже при использовании модулей не может быть "частной" службы, если... модуль загружается с отложенной загрузкой.

В заключение,

Хотя этот синтаксис немного более сложен, чем оригинал, он гарантирует нам, что в корневой модуль добавлен только один экземпляр CreditCardService. Когда CreditCardModule загружен (даже загружен ленивым), новый экземпляр этой службы не будет добавлен к дочернему инжектору.

Если экземпляр будет доступен и в корневом инжекторе, как они говорят, что служба "приватизирована"?

Я не совсем понимаю. Кто-то, пожалуйста, уточните.

4 ответа

providedIn: 'root' это самый простой и эффективный способ предоставления услуг, начиная с Angular 6:

  1. Служба будет доступна для всего приложения в виде отдельного файла, без необходимости добавлять его в массив поставщиков модуля (например, Angular <= 5).
  2. Если служба используется только в модуле с отложенной загрузкой, она будет загружаться с отложенным модулем
  3. Если он никогда не используется, он не будет содержаться в сборке (дерево потрясено).

Для получения дополнительной информации рассмотрите чтение документации и часто задаваемых вопросов по NgModule.

Btw:

  1. Если вы не хотите использовать одноэлементное приложение для всего приложения, используйте вместо него массив поставщика.
  2. Если вы хотите ограничить область действия, чтобы никакой другой разработчик никогда не использовал вашу службу за пределами определенного модуля, используйте вместо этого массив поставщика NgModule.

Эта ветка довольно старая, но я отвечу на то, что узнал, когда искал в этой теме будущих спотыкателей в этой теме.

Концепция приватизации службы с отложенной загрузкой является правильной и по следующим причинам:

  • Когда модуль загружается лениво, он создает собственный контекст Injector, который является дочерним для корневого инжектора (точнее, его родительского инжектора). Службы в них не будут отправлены в корневой инжектор, так как они не были созданы при настройке корневого инжектора.
  • Angular Doc говорит, что один из способов охвата ваших услуг - предоставить их своему собственному модулю (предположим, Module-A). И только когда любой другой модуль B импортирует модуль A, он будет иметь поставщика этой услуги (из модуля A) и, таким образом, сможет получить к нему доступ. Это на самом деле работает для ленивых модулей, а не для активных модулей по следующим причинам:

  • Когда вы реализуете описанный выше метод определения объема для активных модулей, он создаст провайдера для служб этого модуля (предположим, модуль A). Но когда этот конкретный модуль "А" импортируется в корневой модуль (как и все активные модули), корневой инжектор создаст один экземпляр этой службы и откажется от любого дублированного экземпляра этой службы в области действия корневого инжектора (если модуль A был импортирован в любой другой модуль) Таким образом, все активные модули имеют доступ к одноэлементному сервису любого модуля, который был импортирован в корневой модуль.

  • Снова при загрузке приложения корневой инжектор и модуль не будут знать о ленивом модуле и его сервисах. Таким образом, ленивые сервисы приватизируются в своем собственном модуле. Теперь, чтобы корневой модуль имел доступ к отложенному сервису, он должен следовать угловому способу импорта модуля. Который в основном импортирует модуль "предполагается, что он лениво загружен" в корневой модуль во время загрузки приложения, и, таким образом, побеждает цель ленивой загрузки.
  • Если вы все еще хотите иметь доступ к отложенному сервису из корневого инжектора. Вы можете использовать:

    @Injectable({ 
        providedIn: 'root'
    })
    

декоратор в ленивом сервисе и закачка его в корневой инжектор без загрузки ленивого модуля при загрузке приложения.

Пример, который вы использовали, не является истинной реализацией отложенной загрузки, если у вас есть доступ к отложенным службам в корневом модуле без providedIn: root объект. Вы можете перейти по этой ссылке: https://angular.io/guide/providers

Вот как я это делаю: https://stackblitz.com/edit/angular-lazy-service-module?file=src%2Fapp%2Fapp.component.ts

Это доказательство концепции. Вам нужно следить за тем, какой инжектор вы используете (в случае, если ленивому сервису нужны некоторые зависимости) и как вы управляете жизненным циклом ленивого загруженного сервиса (сколько экземпляров вы создаете и т. Д.).

Мой пример использования заключается в том, что существует довольно большой сервис (экспорт в Excel, более 400 КБ gziped), который используется во многих областях приложения, но я не хочу загружать / анализировать его, пока он действительно не понадобится - более быстрая начальная загрузка! (Я также использовал стратегию предварительной загрузки с задержкой, которая загружает модули через несколько секунд).

Основная идея заключается в том, что вы определяете его как ленивый модуль на маршруте (который вы на самом деле не используете), но вы запускаете загрузку вручную. Вы также разрешаете службу из этого модуля (если она у вас есть) с помощью токена инъекции.

ленивый модуль

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

ленивый сервис

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"')
}

Лучшее объяснение, которое я могу вам дать, находится в этой статье.

Во всяком случае, вкратце:

  • Все модули объединяются на этапе компиляции.
  • При активной загрузке Angular Compiler все сервисы помещаются в rootInjector, что делает сервис доступным для всего приложения.
    • Если более одного модуля предоставляют услугу с одним и тем же токеном, поставщик, определенный в модуле, который импортирует другие модули, всегда побеждает.
    • Поставщик из последнего импортированного модуля переопределяет поставщиков в предыдущих модулях, за исключением модуля, который их импортирует.
  • При lazyLoaded каждый модуль все еще объединяется в один при компиляции, но для каждого модуля создается инжектор. Исходя из этого, существует иерархия инжекторов, и то, как компонент ищет введенный токен, поднимается по иерархии в поисках ближайшего поставщика для этого токена.
  • forRoot () * это только соглашение, используемое, когда у вашего модуля есть сервисы, которые вы хотите предоставить для всего приложения и других только для дочерних элементов какого-либо модуля.
Другие вопросы по тегам