2.3服务 (Services) 与依赖注入 (Dependency Injection) 2.3 服务 (Services) 与依赖注入 (Dependency Injection) 在 Angular 应用中,服务 (Services) 和依赖注入 (Dependency Injection, DI) 是构建可维护、可测试和可重用代码的关键概念。服务负责处理应用中的业务逻辑,数据获取,以及其他与组件视图无关的任务。依赖注入则是一种设计模式,它允许组件或其他服务以松耦合的方式获取它们需要的依赖项。 2.3.1 什么是服务 (Services)? 服务本质上是 Angular 应用中的类,它们封装了特定的功能,可以在不同的组件之间共享。服务通常用于: 数据访问: 从后端 API 获取数据。
在 Angular 应用中,服务 (Services) 和依赖注入 (Dependency Injection, DI) 是构建可维护、可测试和可重用代码的关键概念。服务负责处理应用中的业务逻辑,数据获取,以及其他与组件视图无关的任务。依赖注入则是一种设计模式,它允许组件或其他服务以松耦合的方式获取它们需要的依赖项。
服务本质上是 Angular 应用中的类,它们封装了特定的功能,可以在不同的组件之间共享。服务通常用于:
数据访问: 从后端 API 获取数据。
业务逻辑: 执行复杂的计算或处理。
状态管理: 维护应用的状态。
日志记录: 记录应用事件。
跨组件通信: 在组件之间传递数据。
使用服务的好处包括:
代码重用: 避免在多个组件中重复编写相同的逻辑。
可维护性: 将业务逻辑与组件视图分离,使代码更易于理解和修改。
可测试性: 可以独立地测试服务,而无需创建组件实例。
步骤 1: 创建服务
使用 Angular CLI 可以快速创建一个服务:
ng generate service my-service
这将创建一个名为 my-service.service.ts 的文件。
步骤 2: 定义服务
在 my-service.service.ts 文件中,定义服务的类。
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root', }) export class MyService { constructor() { } getData(): string { return 'Hello from MyService!'; } }
@Injectable() 装饰器: 标记该类可以被注入到其他类中。providedIn: 'root' 表示该服务在根模块中注册,这意味着 Angular 将创建一个该服务的单一实例,并在整个应用中共享。步骤 3: 在组件中使用服务
要在一个组件中使用服务,需要:
在组件的构造函数中声明服务的依赖项。
Angular 的依赖注入系统会自动将服务的实例传递给组件。
import { Component, OnInit } from '@angular/core'; import { MyService } from './my-service.service'; @Component({ selector: 'app-my-component', template: ` <p>{{ message }}</p> `, styleUrls: ['./my-component.component.css'] }) export class MyComponent implements OnInit { message: string = ''; constructor(private myService: MyService) { } ngOnInit(): void { this.message = this.myService.getData(); } }
private myService: MyService:在构造函数中声明了 MyService 的依赖项。 private 关键字定义了一个私有成员变量 myService,并将其类型设置为 MyService。当 Angular 创建 MyComponent 的实例时,它会自动查找 MyService 的实例,并将其传递给构造函数。
this.myService.getData():调用服务的方法来获取数据。
依赖注入是一种设计模式,用于管理组件及其依赖项之间的关系。它允许组件以松耦合的方式获取它们需要的依赖项,而无需手动创建或查找这些依赖项。
依赖注入容器
Angular 具有一个内置的依赖注入容器,负责创建和管理服务的实例,并将它们注入到需要它们的组件或其他服务中。
依赖注入的类型
Angular 支持多种依赖注入的方式:
构造函数注入: 这是最常用的方式,如上面的例子所示。
属性注入: 使用 @Inject 装饰器将依赖项注入到组件的属性中。(不推荐)
Setter 注入: 使用 setter 方法来设置依赖项。(不推荐)
依赖注入的配置
可以通过以下方式配置依赖注入:
providedIn: 'root': 在 @Injectable() 装饰器中使用 providedIn: 'root' 表示该服务在根模块中注册,这意味着 Angular 将创建一个该服务的单一实例,并在整个应用中共享。
providers 数组: 在模块或组件的 @NgModule() 或 @Component() 装饰器中使用 providers 数组来注册服务。这允许你控制服务的范围和生命周期。
服务提供者 (Providers)
服务提供者 (Providers) 是 Angular 依赖注入系统的核心。它们定义了如何创建和提供服务的实例。
Provider 可以是以下类型:
类提供者: 使用类本身作为 provider。
值提供者: 使用一个常量值作为 provider。
工厂提供者: 使用一个工厂函数来创建服务实例。
别名提供者: 使用一个已存在的服务作为 provider 的别名。
import { NgModule } from '@angular/core'; import { MyService } from './my-service.service'; import { AnotherService } from './another-service.service'; @NgModule({ declarations: [], imports: [], providers: [ MyService, // 类提供者 { provide: 'API_URL', useValue: 'https://api.example.com' }, // 值提供者 { provide: AnotherService, useFactory: () => { // 工厂提供者 return new AnotherService('some data'); }, deps: [] // 依赖项 }, { provide: 'Logger', useExisting: MyService } // 别名提供者 ], bootstrap: [] }) export class AppModule { }
服务提供者范围
服务提供者的范围决定了服务的生命周期和可见性。
根范围: 使用 providedIn: 'root' 在根模块中注册的服务是单例的,在整个应用中只有一个实例。
模块范围: 在模块的 providers 数组中注册的服务在该模块及其子组件中可见。
组件范围: 在组件的 providers 数组中注册的服务只在该组件及其子组件中可见。
HttpClient 服务Angular 提供了一个 HttpClient 服务,用于与后端 API 进行 HTTP 通信。
步骤 1: 导入 HttpClientModule
在 app.module.ts 文件中导入 HttpClientModule。
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { HttpClientModule } from '@angular/common/http'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, HttpClientModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
步骤 2: 注入 HttpClient 服务
在你的服务或组件中注入 HttpClient 服务。
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class DataService { private apiUrl = 'https://jsonplaceholder.typicode.com/todos'; constructor(private http: HttpClient) { } getData(): Observable<any[]> { return this.http.get<any[]>(this.apiUrl); } }
步骤 3: 发送 HTTP 请求
使用 HttpClient 服务发送 HTTP 请求。
import { Component, OnInit } from '@angular/core'; import { DataService } from './data.service'; @Component({ selector: 'app-data-component', template: ` <ul> <li *ngFor="let item of data">{{ item.title }}</li> </ul> `, styleUrls: ['./data-component.component.css'] }) export class DataComponent implements OnInit { data: any[] = []; constructor(private dataService: DataService) { } ngOnInit(): void { this.dataService.getData().subscribe(data => { this.data = data; }); } }
依赖注入提供了许多优势:
松耦合: 组件和服务之间的依赖关系由依赖注入容器管理,减少了组件之间的直接依赖,使代码更易于修改和维护。
可测试性: 可以使用 mock 对象或 stub 对象来替换真实的依赖项,从而更容易地进行单元测试。
可重用性: 服务可以被多个组件共享,避免了代码重复。
可配置性: 可以通过配置依赖注入容器来改变组件的行为,而无需修改组件的代码。
以下是依赖注入的简单图示:
解释:
Component (A): 组件需要使用服务。
Dependency Injection Container (B): 依赖注入容器负责创建和管理服务的实例。
Service (C): 服务提供组件所需的功能。
组件向依赖注入容器请求服务,容器创建服务实例并将其注入到组件中。
服务和依赖注入是 Angular 应用开发中至关重要的概念。通过使用服务,可以将业务逻辑与组件视图分离,提高代码的可重用性和可维护性。依赖注入则提供了一种松耦合的方式来管理组件及其依赖项,使代码更易于测试和配置。理解和掌握服务和依赖注入的概念对于构建高质量的 Angular 应用至关重要。