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上加上一些描述性的元数据,这些元数据的意义是:架构
NgModule
经过declares
来申明了一些组件(component
)和类组件(如directive
和pipe
,下文统称为组件)是隶属于本身模块的,并且这些组件和类组件必须且仅仅只能属于一个NgModule
,就好像每一个人都必须是属于某一个国家。
当你要用到其余NgModule
的组件,就须要import
其余的模块了。可是这里注意,import
的模块不是全部的组件都是可使用的,import
模块必须明确代表了它的哪些组件是能够外用的,这由exports
数组来定义。三者的关系见下图:app
在NgModule
的元数据中还有一个属性叫providers
,这个属性里通常是声明了一些service
。通常状况下咱们是但愿这些service
在模块内的组件中共享的,Angular确实也是如此设计的,由于providers
中申明的services
都是单例模式的。
可是若是咱们要用到其余模块的服务,是否也是像模块间共享组件同样,经过import
把模块引入进来就能够呢?确实如此,可是咱们还须要理解得更深入一些。
在Angular的设计思想里,组件是私有化的,服务是公有化的。你能够这么来理解,把组件看成“人才”,把服务看成“知识”,知识是无国界的,能够共享,可是人才是有国界的,不能随便共享(只有exports
出去的才能够共享)。当外来模块被import
进来后,它的服务就被共享到你的模块中了。
咱们知道,每一个NgModule
都有injector
,里面存放着经过providers
声明过的service
。若是经过imports
导入了外来模块,那么外来模块的服务就都注入到了你所在模块的injectors
中,以下图所示。ide
若是你的app中应用到了懒加载,那么状况就会更加复杂了。由于懒加载模块是在特定的状况下app须要用到的时候才会被加载进来,因此通常状况下懒加载模块下的serivce
不会被imports
到主模块中的,也就不会注入到root injector
的,而是在root injector
下从新开辟了一个child injector
。若是你在主模块和懒加载模块都provide
了一样的service
,那么就会在两个模块中分别拥有不一样的实例。而这每每是开发者不想看到的,由于服务的做用就是共享数据,而此时不知不觉有两个实例存在,每一个实例单独维护一份数据,那么就会形成逻辑上的错误,更可怕的是,不少开发者并不知道这一点。因此记得在懒加载模块中不要注入跟主模块相同的服务,用主模块中的就行了。可是有时候你由于要用到某些特殊指令,又不得不导入相同的模块,好比路由模块,在主模块和懒加载模块甚至是一些特征模块中都须要导入,这个时候,就须要在主模块中用到forRoot
了。那么forRoot
到底起到一个什么做用呢?其实他们只是一个Angular模块中约定俗成的写法,主要是它们有一个返回类型ModuleWithProviders
,其实就是想把module
和providers
给区分开来,它的意思就是告诉导入模块能够共享被导入模块中的组件(被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
实例。设计
在Angular的@Component
里也有个providers
,可是若是你在组件级别注入了service
,那么这个service
就只能在该组件和它的子组件中使用了,别的组件即便在同一个模块中也不能使用这个service
。组件这个时候相似于一个家庭,这个私有的服务其实相似于传家宝,这个传家宝只能在大家这个家庭里传下去,它有很强的排他性,并且每一个组件实例都会独享一份service
,这能够保证这个service
只服务于你这个组件实例,是专属于你本身的“私人银行”。好比有不少个申请表,虽然申请表都是同样的,每一个服务都必须服务于本身的申请表,这种状况下就适合于用到component
级别的provide。code
若是想系统的学习Angular的模块和依赖注入知识,建议去Angular官网好好研读。我这里只是学习事后加入了本身的理解,又因为我最近在作关于分包加载涉及到Angular Module和DI比较多,也比较有体会,遂成此拙文。component