Angular:使用服务

时间:2018-01-10 11:51:21

标签: angular angular2-services angular2-modules angular2-providers

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

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

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

在上下文中使用方案1,

  • 如果急切加载子模块,则会将一个服务实例添加到根注入器。
  • 如果子模块被延迟加载,则会将一个服务实例添加到根注入器中,并将一个新的服务实例添加到子注入器中,这不是通常的用例。

在上下文中使用方案2,

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

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

他们提到了以下内容。

一开始,

  

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

最后,

  

虽然这种语法比原始语法更复杂,但它   将保证我们只有一个CreditCardService实例   添加到根模块。加载CreditCardModule时(甚至   延迟加载),不会添加该服务的新实例   儿童注射器。

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

我很困惑。有人请澄清。

5 个答案:

答案 0 :(得分:10)

该线程很老,但我会回答在搜索该线程上将来遇到的问题时所学到的知识。

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

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

  • 当您为渴望的模块实现上述范围确定方法时,它将为该模块的服务(假设模块A)创建一个提供程序。但是,当将特定模块'A'导入根模块时(就像所有急切的模块一样),根注入器将创建该服务的单个实例,并将在根注入器的作用域中丢弃该服务的任何重复实例(如果模块A已导入其他任何急切模块中)。因此,所有渴望的模块都可以访问根模块中导入的任何模块的单例服务。

  • 在应用程序加载时,根注入器和模块将不了解惰性模块及其服务。因此,惰性服务在其自己的模块中被私有化。现在,要使根模块能够访问延迟服务,它需要遵循导入模块的方式。基本上,这是在应用程序加载时将“应该延迟加载”模块导入到根模块中,从而达到了延迟加载的目的。
  • 如果您仍然想从根注入器访问懒惰服务。您可以使用:

    @Injectable({ 
        providedIn: 'root'
    })
    

延迟服务中的装饰器,然后将其注入到根注入器中,而不会在应用程序加载时加载延迟模块。

如果您可以在没有providedIn: root对象的情况下访问根模块中的延迟服务,则您所遵循的示例不是延迟加载的真正实现。您可以通过以下链接:https://angular.io/guide/providers#limiting-provider-scope-by-lazy-loading-modules

答案 1 :(得分:5)

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

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

有关更多信息,请考虑阅读documentationNgModule FAQs

顺便说一句:

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

答案 2 :(得分:0)

我能给你的最佳解释是this article

无论如何,简而言之:

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

答案 3 :(得分:0)

答案 4 :(得分:0)

这是我的操作方式: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 {
}

懒惰服务

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