Angular(7) 学习资料 (5)服务

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_20282263/article/details/88029910

服务

简介

服务是一个广义的概念,它包括应用所需的任何值、函数或特性。狭义的服务是一个明确定义了用途的类。它应该做一些具体的事,并做好。

Angular 把组件和服务区分开,以提高模块性和复用性。 通过把组件中和视图有关的功能与其他类型的处理分离开,你可以让组件类更加精简、高效。

理想情况下,组件的工作只管用户体验,而不用顾及其它。 它应该提供用于数据绑定的属性和方法,以便作为视图(由模板渲染)和应用逻辑(通常包含一些模型的概念)的中介者。

组件应该把诸如从服务器获取数据、验证用户输入或直接往控制台中写日志等工作委托给各种服务。通过把各种处理任务定义到可注入的服务类中,你可以让它被任何组件使用。 通过在不同的环境中注入同一种服务的不同提供商,你还可以让你的应用更具适应性。

Angular 不会强迫你遵循这些原则。Angular 只会通过依赖注入来帮你更容易地将应用逻辑分解为服务,并让这些服务可用于各个组件中。

创建服务

import { Injectable } from '@angular/core';

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

提供服务

对于要用到的任何服务,你必须至少注册一个提供商。服务可以在自己的元数据中把自己注册为提供商,这样可以让自己随处可用。或者,你也可以为特定的模块或组件注册提供商。要注册提供商,就要在服务的 @Injectable() 装饰器中提供它的元数据,或者在@NgModule()@Component() 的元数据中

依赖注入

你可以在三种位置之一设置元数据,以便在应用的不同层级使用提供商来配置注入器:

  • 在服务本身的 @Injectable() 装饰器中
  • 在 NgModule 的 @NgModule() 装饰器中
  • 在组件的 @Component() 装饰器中

@Injectable() 装饰器具有一个名叫 providedIn 的元数据选项,在那里你可以指定把被装饰类的提供商放到 root 注入器中,或某个特定 NgModule 的注入器中。

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

@Injectable 级注入器

import { Injectable } from '@angular/core';
import { MyModule } from 'MyModule';

@Injectable({
  // 指定到根级模块
  // 这和在 NgModule 本身的装饰器上配置注入器没有多少不同,主要的区别是如果 NgModule 没有用到该服务,那么它就是可以被摇树优化掉的
  providedIn: 'root'

  
  // 指定到具体的某个模块
  // 这种方式提供的服务无法被摇树优化掉
  providedIn: MyModule
})

@NgModule 级注入器

一般来说,你不必在 providedIn 中指定 AppModule,因为应用中的 root 注入器就是 AppModule 注入器。 不过,如果你在 AppModule 的 @NgModule() 元数据中配置了全应用级的提供商,它就会覆盖通过 @Injectable() 配置的那一个。 你可以用这种方式来为那些供多个应用使用的服务指定非默认的提供商。

providers: [
  { provide: LocationStrategy, useClass: HashLocationStrategy }
]

@Component 级注入器

NgModule 中每个组件都有它自己的注入器。 通过使用 @Component 元数据在组件级配置某个提供商,你可以把这个提供商的范围限定到该组件及其子组件。

@Component({
  selector: 'app-heroes',
  providers: [ HeroService ],
  template: `
    <h2>Heroes</h2>
    <app-hero-list></app-hero-list>
  `
})

依赖提供商

providers: [Logger]

providers: [{ provide: Logger, useClass: Logger }]
  • provide 属性存有令牌,它作为一个 key ,在定位依赖值和配置注入器时使用。
  • 第二个属性是一个提供商定义对象,它告诉注入器要如何创建依赖值。 提供商定义对象中的 key 可以是 useClass —— 就像这个例子中一样。 也可以是 useExisting、useValue 或 useFactory。 每一个 key 都用于提供一种不同类型的依赖,我们稍后会讨论。

替代类提供商

不同的类都可用于提供相同的服务。 比如,下面的代码告诉注入器,当组件使用 Logger 令牌请求日志对象时,给它返回一个 BetterLogger 实例。

[{ provide: Logger, useClass: BetterLogger }]

别名类提供商

假设老的组件依赖于 OldLogger 类。OldLogger 和 NewLogger 的接口相同,但是由于某种原因,我们没法修改老的组件来使用 NewLogger。

当老的组件要使用 OldLogger 记录信息时,你可能希望改用 NewLogger 的单例来处理它。 在这种情况下,无论某个组件请求老的 logger 还是新的 logger,依赖注入器都应该注入这个 NewLogger 的单例。 也就是说 OldLogger 应该是 NewLogger 的别名。

[{ provide: OldLogger, useExisting: NewLogger}]

值类提供商

有时候,提供一个现成的对象会比要求注入器从类去创建更简单一些。 如果要注入一个你已经创建过的对象,请使用 useValue 选项来配置该注入器。

import { InjectionToken } from '@angular/core';
export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');

export const HERO_DI_CONFIG: AppConfig = {
  apiEndpoint: 'api.heroes.com',
  title: 'Dependency Injection'
};

[{ provide: AppConfig, useValue: HERO_DI_CONFIG }] // 无效, 你不能用 TypeScript 的接口作为令牌
[{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }]

工厂类提供商

有时候你需要动态创建依赖值,创建时需要的信息你要等运行期间才能拿到。 比如,你可能需要某个在浏览器会话过程中会被反复修改的信息,而且这个可注入服务还不能独立访问这个信息的源头。这种情况下,你可以使用工厂提供商。

let heroServiceFactory = (logger: Logger, userService: UserService) => {
  return new HeroService(logger, userService.user.isAuthorized);
};

{
  provide: HeroService,
  useFactory: heroServiceFactory,
  deps: [Logger, UserService]
}
  • useFactory 字段告诉 Angular 该提供商是一个工厂函数,该函数的实现代码是 heroServiceFactory。
  • deps 属性是一个提供商令牌数组。 Logger 和 UserService 类作为它们自己的类提供商令牌使用。 注入器解析这些令牌,并把与之对应的服务注入到相应的工厂函数参数表中

预定义令牌与多提供商

Angular 提供了一些内置的注入令牌常量,你可以用它们来自定义系统的多种行为。

比如,你可以使用下列内置令牌来切入 Angular 框架的启动和初始化过程。 提供商对象可以把任何一个注入令牌与一个或多个用来执行应用初始化操作的回调函数关联起来。

  • PLATFORM_INITIALIZER:平台初始化之后调用的回调函数。
  • APP_BOOTSTRAP_LISTENER:每个启动组件启动完成之后调用的回调函数。这个处理器函数会收到这个启动组件的 ComponentRef 实例。
  • APP_INITIALIZER:应用初始化之前调用的回调函数。注册的所有初始化器都可以(可选地)返回一个 Promise。所有返回 Promise 的初始化函数都必须在应用启动之前解析完。如果任何一个初始化器失败了,该应用就不会继续启动。

该提供商对象还有第三个选项 multi: true,把它和 APP_INITIALIZER 一起使用可以为特定的事件注册多个处理器。

export const APP_TOKENS = [
 { provide: PLATFORM_INITIALIZER, useFactory: platformInitialized, multi: true    },
 { provide: APP_INITIALIZER, useFactory: delayBootstrapping, multi: true },
 { provide: APP_BOOTSTRAP_LISTENER, useFactory: appBootstrapped, multi: true },
];

可摇树优化的提供商

理想情况下,如果应用没有注入服务,它就不应该包含在最终输出中。 不过,Angular 要能在构建期间识别出该服务是否需要。 由于还可能用 injector.get(Service) 的形式直接注入服务,所以 Angular 无法准确识别出代码中可能发生此注入的全部位置,因此为保险起见,只能把服务包含在注入器中。 因此,在 NgModule 或 组件级别提供的服务是无法被摇树优化掉的。

// 可摇树
@Injectable({
  providedIn: 'root',
  useFactory: () => new Service('dependency') // 还可以通过配置工厂函数来实例化
})
export class Service {}

要想覆盖可摇树优化的提供商,请使用其它提供商来配置指定的 NgModule 或组件的注入器,只要使用 @NgModule() 或 @Component() 装饰器中的 providers: [] 数组就可以了。

猜你喜欢

转载自blog.csdn.net/qq_20282263/article/details/88029910
今日推荐