Angular模块与依赖注入

Angular模块划分和依赖注入的思想能够说是Angular架构核心中的核心,最近通读了一遍Angular官网上的模块和依赖注入两章,也领会到了其中的一些精髓。bootstrap

Angular的模块NgModule,其做用其实有点相似于Jave的package和C#的namespace,是代码组织和代码分割的一种形式。下面是Angular官网上的一段经典的NgModule的写法:数组

@NgModule({
  declarations: [
    AppComponent,
    ItemDirective
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

能够看出,NgModule其实是在一个普通的class上加上一些描述性的元数据,这些元数据的意义是:架构

  • declarations: 声明某些组件、指令和管道属于这个模块。
  • exports: 公开其中的部分组件、指令和管道,以便其它模块中的组件模板中可使用它们。
  • imports: 导入其它带有组件、指令和管道的模块,这些模块中的元件都是本模块所需的。
  • providers: 提供一些供应用中的其它组件使用的服务。

1. 模块间分享组件

NgModule经过declares来申明了一些组件(component)和类组件(如directivepipe,下文统称为组件)是隶属于本身模块的,并且这些组件和类组件必须且仅仅只能属于一个NgModule,就好像每一个人都必须是属于某一个国家。
当你要用到其余NgModule的组件,就须要import其余的模块了。可是这里注意,import的模块不是全部的组件都是可使用的,import模块必须明确代表了它的哪些组件是能够外用的,这由exports数组来定义。三者的关系见下图:app

clipboard.png

2. 模块间分享服务

NgModule的元数据中还有一个属性叫providers,这个属性里通常是声明了一些service。通常状况下咱们是但愿这些service在模块内的组件中共享的,Angular确实也是如此设计的,由于providers中申明的services都是单例模式的。
可是若是咱们要用到其余模块的服务,是否也是像模块间共享组件同样,经过import把模块引入进来就能够呢?确实如此,可是咱们还须要理解得更深入一些。
在Angular的设计思想里,组件是私有化的,服务是公有化的。你能够这么来理解,把组件看成“人才”,把服务看成“知识”,知识是无国界的,能够共享,可是人才是有国界的,不能随便共享(只有exports出去的才能够共享)。当外来模块被import进来后,它的服务就被共享到你的模块中了。
咱们知道,每一个NgModule都有injector,里面存放着经过providers声明过的service。若是经过imports导入了外来模块,那么外来模块的服务就都注入到了你所在模块的injectors中,以下图所示。ide

clipboard.png

3. 懒加载下的服务共享

若是你的app中应用到了懒加载,那么状况就会更加复杂了。由于懒加载模块是在特定的状况下app须要用到的时候才会被加载进来,因此通常状况下懒加载模块下的serivce不会被imports到主模块中的,也就不会注入到root injector的,而是在root injector下从新开辟了一个child injector。若是你在主模块和懒加载模块都provide了一样的service,那么就会在两个模块中分别拥有不一样的实例。而这每每是开发者不想看到的,由于服务的做用就是共享数据,而此时不知不觉有两个实例存在,每一个实例单独维护一份数据,那么就会形成逻辑上的错误,更可怕的是,不少开发者并不知道这一点。因此记得在懒加载模块中不要注入跟主模块相同的服务,用主模块中的就行了。可是有时候你由于要用到某些特殊指令,又不得不导入相同的模块,好比路由模块,在主模块和懒加载模块甚至是一些特征模块中都须要导入,这个时候,就须要在主模块中用到forRoot了。那么forRoot到底起到一个什么做用呢?其实他们只是一个Angular模块中约定俗成的写法,主要是它们有一个返回类型ModuleWithProviders,其实就是想把moduleproviders给区分开来,它的意思就是告诉导入模块能够共享被导入模块中的组件(被exports出的组件),可是服务注入一次就行了,之后要是再有一样的服务须要注入就忽略之,不要建立两个不一样实例的服务。
懒加载模块代码以下:学习

import { NgModule, ModuleWithProviders } from '@angular/core';

import { MyDirective } from './my.directive';
import { FunPipe } from './fun.pipe';
import { SomeService } from './some.service';

@NgModule({
  declarations: [
      FunPipe,
      MyDirective
  ],
  exports: [
      FunPipe,
      MyDirective
  ]
})
export class SharedModule {
    static forRoot(): ModuleWithProviders {
        return {
            ngModule:SharedModule,
            providers:[ SomeService ]
        };
    }
}

咱们在NgModule的元数据中像往常同样声明和导出咱们的管道和指令,可是咱们不提供服务,而是在模块的类中定义一个静态方法forRoot,该方法返回一个实现Angular的 ModuleWithProviders 接口的对象。
在咱们的应用模块中,导入懒加载模块并调用forRoot静态方法来提供咱们的服务:spa

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { SharedModule } from './shared/shared.module';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    SharedModule.forRoot()
  ],
  bootstrap: [
    AppComponent
  ]
})
export class AppModule {}

这样,在导入模块和被导入的懒加载模块中仅仅维护了一个service实例。设计

4. 服务在组件中私有化

在Angular的@Component里也有个providers,可是若是你在组件级别注入了service,那么这个service就只能在该组件和它的子组件中使用了,别的组件即便在同一个模块中也不能使用这个service。组件这个时候相似于一个家庭,这个私有的服务其实相似于传家宝,这个传家宝只能在大家这个家庭里传下去,它有很强的排他性,并且每一个组件实例都会独享一份service,这能够保证这个service只服务于你这个组件实例,是专属于你本身的“私人银行”。好比有不少个申请表,虽然申请表都是同样的,每一个服务都必须服务于本身的申请表,这种状况下就适合于用到component级别的provide。code

总结

若是想系统的学习Angular的模块和依赖注入知识,建议去Angular官网好好研读。我这里只是学习事后加入了本身的理解,又因为我最近在作关于分包加载涉及到Angular Module和DI比较多,也比较有体会,遂成此拙文。component

相关文章
相关标签/搜索