建立服務須要用到Injectable,@Injectable() 裝飾器把類標記爲可供注入的服務,不過在使用該服務的 provider 配置好 Angular 的依賴注入器以前,Angular 實際上沒法將其注入到任何位置。javascript
provider告訴注入器如何建立該服務,能夠經過設置元數據來配置注入器(3種方式):java
@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生產模式所用的優化工具能夠進行搖樹優化,它會移除那些沒有用過的服務,搖樹優化生成的包更小。優化
若是模塊是lazy modole,須要使用@NgModule的provider選項。
不管對於根級注入器仍是模塊級注入器,服務實例的生存期都和應用或模塊自己相同。Angular 能夠把這個服務實例注入到任何須要它的類中(即 app內是單例的)。Angular 只能把相應的服務注入到該組件實例或其下級組件實例中,而不能把這個服務實例注入到其它地方(即 組件內並非單例的)。
當一個組件申請得到一個依賴時,Angular 先嚐試用該組件本身的注入器來知足它。 若是該組件的注入器沒有找到對應的提供商,它就把這個申請轉給它父組件的注入器來處理。 若是那個注入器也沒法知足這個申請,它就繼續轉給它在注入器樹中的父注入器。 這個申請繼續往上冒泡 —— 直到 Angular 找到一個能處理此申請的注入器或者超出了組件樹中的祖先位置爲止。 若是超出了組件樹中的祖先還未找到,Angular 就會拋出一個錯誤。this
在angular中建立單例服務有兩種方式:
這裏第一條很容易理解。重點第二條:當經過@NgMododule()來聲明一個serivce時,這個服務在AppModule內將會是單例的,當一個module中提供了一個service,當另外一個 lazy module導入了這個 模塊時,angular會爲它創一個子注入器,會從新建立service的實例,此service也就多了一個實例。
若是某個模塊同時提供了服務提供商和可聲明對象(組件、指令、管道),那麼當在某個子注入器中加載它的時候(好比lazy module),就會生成多個該服務提供商的實例。 而存在多個實例會致使一些問題,由於這些實例會屏蔽掉根注入器中該服務提供商的實例,而它的本意多是做爲單例對象使用的。 所以,Angular 提供了一種方式來把服務提供商從該模塊中分離出來,以便該模塊既能夠帶着 providers 被根模塊導入,也能夠不帶 providers 被子模塊導入。
如上文所述,當在運行期間加載一個新的 NgModule 時(即lazy module),Angular 也能夠爲它建立一個注入器,因此此時導入的其餘模塊中的service就生成了多個實例,而forRoot能夠保證並不建立新的service實例,而是去引用root注入器中的service實例,也就保證了service依然是個單例服務。
在懶加載模塊中導入有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; }