Angular:延迟加载带有服务的模块

kar*_*una 11 angular2-services angular2-providers angular2-modules angular

我一直在关注教程,了解延迟加载,下面是我的推断.

场景1:通过将它们放在providers子模块的数组中来提供服务

场景2:使用该forRoot方法在子模块中提供服务

方案1在上下文中,

  • 如果急切地加载子模块,则将该服务的实例添加到根注入器.
  • 如果延迟加载子模块,则将服务的实例添加到根注入器,并将新服务实例添加到子注入器,这不是通常的用例.

在上下文中使用方案2

  • 如果急切地加载子模块,则将该服务的实例添加到根注入器.

  • 如果延迟加载子模块,则在根模块和子模块中都可以使用相同的服务实例,这是通常的用例.

他们提到了以下内容.

一开始,

因此,即使使用模块,也无法拥有"私有"服务,除非......模块正在延迟加载.

最后,

尽管此语法比原始语法更复杂,但它将保证我们只将一个CreditCardService实例添加到根模块.加载CreditCardModule(甚至延迟加载)时,不会将该服务的新实例添加到子注入器.

如果实例也将在根注入器中可用,那么他们如何表示该服务被"私有化"?

我糊涂了.有人请澄清一下.

小智 14

这个帖子已经很老了但是我会回答我在这个主题上搜索这个主题时所学到的未来绊脚石.

使用延迟加载对服务进行私有化的概念是正确的,原因如下:

  • 当模块被延迟加载时,它会创建自己的Injector上下文,它是根注入器的子项(它的父注入器是精确的).它们中的服务不会被推送到根注入器,因为在配置根注入器时它们没有被实例化.
  • Angular Doc说,提供服务范围的方法之一是将它们提供给自己的模块(假设模块-A).并且只有当任何其他模块B导入模块A时,它才会拥有该服务的提供者(来自模块A),因此可以访问它.这实际上适用于懒惰模块,而不适用于急切模块,原因如下:

  • 当您为eager模块实现上述范围方法时,它将为该模块的服务创建一个提供程序(假设模块A).但是,当该特定模块"A"被导入到根模块(因为所有渴望模块应该是),根注射器将创建该服务的一个实例,并会丢弃在根注射器的范围该服务的任何重复的情况下(如模块A是在任何其他eager模块中导入的).因此,所有渴望的模块都可以访问在根模块中导入的任何模块的单件服务.

  • 再次在应用程序加载时,根注入器和模块将不知道延迟模块及其服务.因此,懒惰的服务在他们自己的模块中私有化.现在,对于根模块可以访问惰性服务,它需要遵循导入模块的角度方式.这基本上是在应用程序加载时将"应该被延迟加载"模块导入根模块,从而破坏了延迟加载的目的.
  • 如果您仍希望从根注入器访问延迟服务.你可以使用:

    @Injectable({ 
        providedIn: 'root'
    })
    
    Run Code Online (Sandbox Code Playgroud)

延迟服务中的装饰器并将其注入根注入器而不在应用程序加载时加载延迟模块.

如果您可以访问根模块中的惰性服务而没有该providedIn: root对象,那么您所遵循的示例并不是延迟加载的真正实现.您可以浏览以下链接:https://angular.io/guide/providers#limiting-provider-scope-by-lazy-loading-modules


Mic*_*ick 8

providedIn: 'root' 自Angular 6以来,这是最简单,最有效的服务提供方式:

  1. 该服务将在应用程序范围内以单例形式提供,而无需将其添加到模块的提供者数组中(例如Angular <= 5)。
  2. 如果该服务仅在延迟加载的模块中使用,则它将与该模块一起延迟加载
  3. 如果从不使用它,它将不会包含在构建中(摇动的树)。

有关更多信息,请考虑阅读文档NgModule常见问题解答

顺便说一句:

  1. 如果您不希望应用程序范围内的单例,请改用提供程序的组件数组。
  2. 如果要限制范围,以至于没有其他开发人员会在特定模块之外使用您的服务,请改为使用提供程序的NgModule数组。


And*_*tar 6

这是我的做法:https://stackblitz.com/edit/angular-lazy-service-module ?file=src%2Fapp%2Fapp.component.ts

这是一个概念证明。您需要注意您使用的注入器(以防惰性服务需要一些依赖项)以及如何管理惰性加载服务的生命周期(您创建了多少实例等)。

我的用例是,有一个相当大的服务(导出到 Excel,压缩后超过 400 KB),用于应用程序的多个区域,但我不想在实际需要之前加载/解析它 - 更快的初始加载!(实际上我还使用了延迟预加载策略,在几秒钟后加载模块)。

基本思想是,您将其定义为路由中的惰性模块(您实际上并不使用),但手动触发加载。您还可以使用注入令牌解析该模块中的服务(一旦拥有该服务)。

惰性模块

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 {
}
Run Code Online (Sandbox Code Playgroud)

懒惰的服务

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}`;
  }
}
Run Code Online (Sandbox Code Playgroud)

应用程序模块

@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 { }
Run Code Online (Sandbox Code Playgroud)

在组件内部使用它

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"')
}
Run Code Online (Sandbox Code Playgroud)


小智 2

我能给你的最好的解释是在这篇文章中。

无论如何,简而言之:

  • 所有模块都会在编译阶段合并。
  • 当急切加载 Angular Compiler 时,会将所有服务放入 rootInjector 中,从而使该服务可供整个应用程序使用。
    • 如果多个模块提供具有相同令牌的服务,则导入其他模块的模块中定义的提供者总是获胜。
    • 最后导入模块中的提供程序会覆盖前面模块中的提供程序(导入它们的模块除外)。
  • 当lazyLoaded时,每个模块仍然在编译时合并为一个,但会为每个模块创建一个注入器。由此,存在注入器的层次结构,并且组件查找注入令牌的方式正在攀爬层次结构,寻找该令牌的更接近的提供者。
  • forRoot()* 它只是当您的模块具有您想要为整个应用程序提供的服务以及仅为某些模块的子级提供的服务时使用的约定。