首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

服务提供商

前提条件:

要想查看本页提到的这个带有特性模块的范例应用,参见 在线例子 / 下载范例

提供商就相当于说明书,用来指导 DI 系统该如何获取某个依赖的值。 大多数情况下,这些依赖就是你要创建和提供的那些服务。

提供服务

如果你是用 CLI 创建的应用,那么可以使用下列 CLI 命令在项目根目录下创建一个服务。把其中的 User 替换成你的服务名。

代码语言:javascript
复制
content_copyng generate service User

该命令会创建下列 UserService 骨架:

src/app/user.service.0.ts

代码语言:javascript
复制
content_copyimport { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class UserService {
}

现在,你就可以在应用中到处注入 UserService 了。

该服务本身是 CLI 创建的一个类,并且加上了 @Injectable 装饰器。默认情况下,该装饰器是用 providedIn 属性进行配置的,它会为该服务创建一个提供商。在这个例子中,providedIn: 'root' 指定该服务应该在根注入器中提供。

提供商的作用域

当你把服务提供商添加到应用的根注入器中时,它就在整个应用程序中可用了。 另外,这些服务提供商也同样对整个应用中的类是可用的 —— 只要它们有供查找用的服务令牌。

你应该始终在根注入器中提供这些服务 —— 除非你希望该服务只有在消费方要导入特定的 @NgModule 时才生效。

providedIn 与 NgModule

也可以规定某个服务只有在特定的 @NgModule 中提供。比如,如果你你希望只有当消费方导入了你创建的 UserModule 时才让 UserService 在应用中生效,那就可以指定该服务要在该模块中提供:

src/app/user.service.1.ts

代码语言:javascript
复制
content_copyimport { Injectable } from '@angular/core';
import { UserModule } from './user.module';

@Injectable({
  providedIn: UserModule,
})
export class UserService {
}

上面的例子展示的就是在模块中提供服务的首选方式。之所以推荐该方式,是因为当没有人注入它时,该服务就可以被摇树优化掉。如果没办法指定哪个模块该提供这个服务,你也可以在那个模块中为该服务声明一个提供商:

src/app/user.module.ts

代码语言:javascript
复制
content_copyimport { NgModule } from '@angular/core';

import { UserService } from './user.service';

@NgModule({
  providers: [UserService],
})
export class UserModule {
}

使用惰性加载模块限制提供商的作用域

在 CLI 生成的基本应用中,模块是立即加载的,这意味着它们都是由本应用启动的,Angular 会使用一个依赖注入体系来让一切服务都在模块间有效。对于立即加载式应用,应用中的根注入器会让所有服务提供商都对整个应用有效。

当使用惰性加载时,这种行为需要进行改变。惰性加载就是只有当需要时才加载模块,比如路由中。它们没办法像立即加载模块那样进行加载。这意味着,在它们的 providers 数组中列出的服务都是不可用的,因为根注入器并不知道这些模块。

当 Angular 的路由器惰性加载一个模块时,它会创建一个新的注入器。这个注入器是应用的根注入器的一个子注入器。想象一棵注入器树,它有唯一的根注入器,而每一个惰性加载模块都有一个自己的子注入器。路由器会把根注入器中的所有提供商添加到子注入器中。如果路由器在惰性加载时创建组件, Angular 会更倾向于使用从这些提供商中创建的服务实例,而不是来自应用的根注入器的服务实例。

任何在惰性加载模块的上下文中创建的组件(比如路由导航),都会获取该服务的局部实例,而不是应用的根注入器中的实例。而外部模块中的组件,仍然会收到来自于应用的根注入器创建的实例。

虽然你可以使用惰性加载模块来提供实例,但不是所有的服务都能惰性加载。比如,像路由之类的模块只能在根模块中使用。路由器需要使用浏览器中的全局对象 location 进行工作。

使用组件限定服务提供商的作用域

另一种限定提供商作用域的方式是把要限定的服务添加到组件的 providers 数组中。组件中的提供商和 NgModule 中的提供商是彼此独立的。 当你要立即加载一个自带了全部所需服务的模块时,这种方式是有帮助的。 在组件中提供服务,会限定该服务只能在该组件中有效(同一模块中的其它组件不能访问它)。

src/app/app.component.ts

代码语言:javascript
复制
content_copy@Component({
/* . . . */
  providers: [UserService]
})

在模块中提供服务还是在组件中?

通常,要在根模块中提供整个应用都需要的服务,在惰性加载模块中提供限定范围的服务。

路由器工作在根级,所以如果你把服务提供商放进组件(即使是 AppComponent)中,那些依赖于路由器的惰性加载模块,将无法看到它们。

当你必须把一个服务实例的作用域限定到组件及其组件树中时,可以使用组件注册一个服务提供商。 比如,用户编辑组件 UserEditorComponent,它需要一个缓存 UserService 实例,那就应该把 UserService 注册进 UserEditorComponent 中。 然后,每个 UserEditorComponent 的实例都会获取它自己的缓存服务实例。

关于 NgModule 的更多知识

你还可能对下列内容感兴趣:

扫码关注腾讯云开发者

领取腾讯云代金券

http://www.vxiaotou.com