angular 服务随记

angular 服务随记


依赖注入

建立服务须要用到Injectable,@Injectable() 装饰器把类标记为可供注入的服务,不过在使用该服务的 provider 配置好 Angular 的依赖注入器以前,Angular 实际上没法将其注入到任何位置。javascript

provider告诉注入器如何建立该服务,能够经过设置元数据来配置注入器(3种方式):java

  • 在服务自己的 @Injectable() 装饰器中。
  • 在 NgModule 的 @NgModule() 装饰器中。
  • 在组件的 @Component() 装饰器中。

@Injectable() 装饰器具备一个名叫 providedIn 的元数据选项,在这里指定把被装饰类的provider放到 root 注入器中,或某个特定 NgModule 的注入器中。编程

@NgModule() 和 @Component() 装饰器都有用一个 providers 元数据选项,在那里你能够配置 NgModule 级或组件级的注入器。app

注入器与服务实例

在某个注入器范围内,服务是单例的。应用只有一个根注入器,angular具备多级注入器系统,觉得者下级注入器能够建立本身的服务实例。ide

每当 Angular 建立一个在 @Component() 中指定了 providers 的组件实例时,它也会为该实例建立一个新的子注入器。 相似的,当在运行期间加载一个新的 NgModule 时(即lazy module),Angular 也能够为它建立一个拥有本身的提供商的注入器模块化

借助注入器继承机制,仍然能够把全应用级的服务注入到这些组件中。 组件的注入器是其父组件注入器的子节点,也是其父节点的父节点的后代,以此类推,直到应用的根注入器为止。 Angular 能够注入该继承谱系中任何一个注入器提供的服务工具

模块化编程时,service、component、pipe等最好都放在module中,须要引入这些服务时,经过导入module来引用,不要直接import service 和component,这不符合模块化思想。

多级注入系统

应用程序中有一个与组件树平行的注入器树,对于在什么级别上注入会最终致使:测试

  • 最终包的大小
  • 服务的范围
  • 服务的生命周期

当在服务自身的@Injectable()装饰器中指定provider时,CLI生产模式所用的优化工具能够进行摇树优化,它会移除那些没有用过的服务,摇树优化生成的包更小。优化

三级provider

  • root级,是AppModule全局的,配置方法已提。
  • NgModule级,两种方法:能够在module的@NgModule 的 provider 元数据中指定;也能够在@injectable() 的providerIn选项中指定某个模块类
若是模块是lazy modole,须要使用@NgModule的provider选项。
  • 组件级为每一个component实例配置本身的注入器
不管对于根级注入器仍是模块级注入器,服务实例的生存期都和应用或模块自己相同。Angular 能够把这个服务实例注入到任何须要它的类中(即 app内是单例的)。Angular 只能把相应的服务注入到该组件实例或其下级组件实例中,而不能把这个服务实例注入到其它地方(即 组件内并非单例的)。

注入器冒泡

当一个组件申请得到一个依赖时,Angular 先尝试用该组件本身的注入器来知足它。 若是该组件的注入器没有找到对应的提供商,它就把这个申请转给它父组件的注入器来处理。 若是那个注入器也没法知足这个申请,它就继续转给它在注入器树中的父注入器。 这个申请继续往上冒泡 —— 直到 Angular 找到一个能处理此申请的注入器或者超出了组件树中的祖先位置为止。 若是超出了组件树中的祖先还未找到,Angular 就会抛出一个错误。this

单例服务

在angular中建立单例服务有两种方式:

  • 在建立服务时声明该服务在应用的根上提供
  • 把该服务包含在AppModule或者某个只会被AppModule导入的模块中
这里第一条很容易理解。重点第二条:当经过@NgMododule()来声明一个serivce时,这个服务在AppModule内将会是单例的,当一个module中提供了一个service,当另外一个 lazy module导入了这个 模块时,angular会为它创一个子注入器,会从新建立service的实例,此service也就多了一个实例。

forRoot()

若是某个模块同时提供了服务提供商和可声明对象(组件、指令、管道),那么当在某个子注入器中加载它的时候(好比lazy module),就会生成多个该服务提供商的实例。 而存在多个实例会致使一些问题,由于这些实例会屏蔽掉根注入器中该服务提供商的实例,而它的本意多是做为单例对象使用的。 所以,Angular 提供了一种方式来把服务提供商从该模块中分离出来,以便该模块既能够带着 providers 被根模块导入,也能够不带 providers 被子模块导入。

如上文所述,当在运行期间加载一个新的 NgModule 时(即lazy module),Angular 也能够为它建立一个注入器,因此此时导入的其余模块中的service就生成了多个实例,而forRoot能够保证并不建立新的service实例,而是去引用root注入器中的service实例,也就保证了service依然是个单例服务。

Code

在懒加载模块中导入有service的TestDIModule模块

@NgModule({
  imports: [
    CommonModule,
    BatteryRoutingModule,
    TestDIModule
  ],
  declarations: [BatteryWidgetComponent, BatteryTwoComponent,
    DemoComponent]
})

在TestDIModule模块中

@NgModule({
  imports: [
    CommonModule
  ],
  declarations: [TestDiComponent],
  exports: [TestDiComponent],
  providers: [  ]
})
export class TestDIModule {
  static forRoot(): ModuleWithProviders {
    return {
      ngModule: TestDIModule,
      providers: [
        TestDiService
      ]
    };
  }
 }

在根模块中引入TestDIModule模块

imports: [
    BrowserModule,
    TestDIModule.forRoot(),
  ],

最后在根模块路由中添加这个懒加载模块

const routes: Routes = [
  { path: 'battery', loadChildren: './battery-widget/battery.widget.module#BatteryWidgetModule' },
];

@NgModule({
  exports: [ RouterModule ],
  imports: [ RouterModule.forRoot(routes)
  ],
})

做为测试,能够在TestDIModule中的service中打log看一下

import { Injectable, ModuleWithProviders } from '@angular/core';
import { TestDIModule } from './test-di.module'

@Injectable()
export class TestDiService {

  constructor() {
    console.log('->TestDiService');
  }

  addCoount() {
    this.count++;
    console.log('->count', this.count);
  }

  count = 0;
}
相关文章
相关标签/搜索