如何在 Angular 中实现服务和依赖注入
介绍
服务是充当中央存储库的 Angular 类。它们可用于在应用程序中共享通用代码。服务有助于使我们的组件代码更加简洁。因此,组件应主要负责呈现用户交互的用户界面 - 即事件和相关内容。其他繁重的任务(如从某些 API 服务器获取数据、验证、将警告/错误消息记录到控制台等)可以通过服务处理。因此,进行 AJAX 调用的代码可以在服务和消费组件中处理,它们只需要能够使用该服务并知道响应何时准备就绪。服务使用称为依赖注入 (DI) 的机制连接在一起。我们只需要有一个可注入的服务类,以便能够将这些服务方法共享给任何消费组件。此外,我们的 Angular 组件/服务中的 DI 可以使用构造函数或注入器来实现。
创建服务
让我们尝试创建一个基本服务,我们将其称为 LoggingService。它将包含一个方法来记录一些预定义的字符串并附加传递给它的任何参数。
在logging.service.ts中:
export class LoggingService {
logSomeMessage(msg: any) {
console.log("Message from consumer is : " + msg);
}
}
消费服务
现在假设我们希望能够在其他组件中使用上述服务,例如从我们的 AppComponent。
因此,我们的消费组件看起来如下所示:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { LoggingService } from './logging.service.ts';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
providers: [LoggingService]
})
export class AppComponent implements OnInit, OnDestroy {
constructor(private loggingService: LoggingService) {
this.loggingService.logSomeMessage("Hi from AppComponent !")
}
}
因此,为了能够使用 LoggingService,我们需要做一些不同的事。
将 LoggingService 类导入到 AppComponent。
将 LoggingService 依赖项作为参数添加到 AppComponent 的构造函数中。
LoggingService 由 AppComponent 内部提供
子组件中的服务
Angular DI 实际上是一个分层注入器。因此,我们可以有以下场景:
如果在 AppModule 中注入了服务,则该服务的同一实例可在整个应用程序范围内使用。
如果在 AppComponent 中注入了服务,则该服务的同一实例可供 AppComponent 及其所有子组件使用。需要注意的是,实例不会向上传播,它们只会向下传播到子组件。
如果在任何其他组件中注入服务,则该组件及其所有子组件都可以使用同一服务实例。
因此,每当 Angular 需要实例化服务类时,它都会在 DI 框架上进行查找以解决该依赖关系。默认情况下,DI 会从组件的本地注入器开始搜索提供程序,然后通过注入器树向上冒泡;此过程一直持续到到达根注入器。
第一个配置了提供程序的注入器将依赖项提供给我们的构造函数。如果我们没有任何提供程序一直到根注入器,Angular DI 框架就会抛出错误。
假设我们有一个 AppComponent 的子组件。我们将其称为 AccountComponent。
以下是我们的 AccountComponent 的样子:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { LoggingService } from './logging.service.ts';
@Component({
selector: 'account-component',
templateUrl: './account.component.html',
providers: [LoggingService]
})
export class AccountComponent implements OnInit, OnDestroy {
constructor(private loggingService: LoggingService) {
this.loggingService.logSomeMessage("Hi from AccountComponent !")
}
}
在这里,我们导入 LoggingService 并将其作为参数传递给 AccountComponent 的构造函数,告诉 Angular 我们需要此组件内的 LoggingService。但我们也在“providers”数组中指定了这一点。当 Angular 看到它时,它将提供一个新的 LoggingService 实例,覆盖通过 Angular 依赖注入自动接收的实例。在某些情况下,这很可能是必需的行为,因此这样做没有错。但是,如果我们希望即使对子组件也使用相同的loggingService实例(大多数情况下应该如此),我们不应该在“providers”数组中指定该服务。因此,如果我们想使用相同的服务实例,我们的完整代码将如下所示:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { LoggingService } from './logging.service.ts';
@Component({
selector: 'account-component',
templateUrl: './account.component.html'
})
export class AccountComponent implements OnInit, OnDestroy {
constructor(private loggingService: LoggingService) {
this.loggingService.logSomeMessage("Hi from AccountComponent !")
}
}
提供服务
提供 Angular 服务的方式有很多种。我们已经看到了其中一种方法,即上面的方法,它是在组件级别完成的,通过使用 @Component 装饰器在提供程序中指定服务。
我们还可以提供服务并将其注册到 @NgModule 的提供程序数组中。下面是相同的示例(假设我们在 UserModule 中提供它):
import { NgModule } from '@angular/core';
import {LoggingService} from './logging.service';
...
@NgModule({
imports: [ BrowserModule],
declarations: [ AppComponent],
bootstrap: [ AppComponent],
providers: [LoggingService]
})
指定上述内容将确保我们的服务的相同实例可供该模块中的所有组件使用。
上述方法类似于在该服务中使用 @Injectable,例如,如果我们不希望我们的 MyUserService 可供应用程序使用,除非它们明确导入我们的 MyUserModule,我们可以在 MyUserService 中按如下方式指定:
import { Injectable } from '@angular/core';
import { MyUserModule } from './my.user.module';
@Injectable({
providedIn: MyUserModule,
})
export class MyUserService {
}
我们还可以在 AppModule 中指定全局服务。下面是相同的示例:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { LoggingService } from './logging.service';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
ReactiveFormsModule
],
providers: [
LoggingService
],
bootstrap: [AppComponent]
})
export class AppModule {}
提供服务的推荐方法是使用 @Injectable 装饰器内的 providedIn。为了能够在整个应用程序中全局使用该服务,我们使用以下语法:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class LoggingService {
}
通过上述内容,我们可以将 LoggingService 注入到应用程序的任何位置。
服务和模块
对于加载模块,Angular 有两种截然不同的策略:
预先加载模块
惰性加载模块
急切加载的模块被导入到根 AppModule 中。它们的包最初会与 AppModule 一起下载,因此这不是最佳策略,但对于较小的应用程序来说,这应该不是问题。另一方面,延迟加载的模块会在请求时加载,例如,如果我们有一个名为 ShoppingModule 的模块,我们可以将其转换为延迟加载的模块来控制其捆绑方式。因此,只有当用户访问购物页面时,才会下载该模块,这大大减少了初始包的大小。这对于可能包含许多不同模块的大型应用程序确实很有用,如果我们知道用户不一定会访问我们应用程序的某些区域,我们可以将它们转换为延迟加载的模块。
现在,让我们看看模块加载策略对服务有何影响。
我们将研究在积极加载模块和消极加载模块中提供服务的不同示例,并观察不同的行为。
基本设置是相同的;我们有一个 CoreModule,它是一个积极加载的模块,而 ShoppingListModule 是延迟加载的。
让我们看一下我们的 LoggingService 代码。
export class LoggingService {
lastLog: string,
printMessage(msg: string) {
console.log("Current message is " + msg);
console.log("Last logged message is " + this.lastLog);
this.lastLog = msg;
}
}
假设我们想要在 AppComponent 和 ShoppingListComponent 中使用该服务,而该服务是通过使用logging.service中的@Injectable提供的。
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class LoggingService {
lastLog: string,
printMessage(msg: string) {
console.log("Current message is : " + msg);
console.log("Last logged message is : " + this.lastLog);
this.lastLog = msg;
}
}
import { Component, OnInit, OnDestroy } from '@angular/core';
import { LoggingService } from './logging.service.ts';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit, OnDestroy {
constructor(private loggingService: LoggingService) {
}
ngonInit(){
this.loggingService.printMessage("Hello from AppComponent !")
}
}
import { Component, OnInit, OnDestroy } from '@angular/core';
import { LoggingService } from './logging.service.ts';
@Component({
selector: 'shopping-list',
templateUrl: './shopping-list.component.html'
})
export class ShoppingListComponent implements OnInit, OnDestroy {
constructor(private loggingService: LoggingService) {
}
ngonInit(){
this.loggingService.printMessage("Hello from ShoppingListComponent !")
}
}
通过上述设置,假设用户最初位于主页,然后导航到购物页面。这样,我们会看到控制台中记录以下输出:
Current message is : Hello from AppComponent !
Last logged message is : undefined
Current message is : Hello from ShoppingListComponent !
Last logged message is : Hello from AppComponent !
因为从 ShoppingListComponent 中,我们可以看到之前的消息被正确记录,这证明我们在 AppComponent 和 ShoppingListComponent 中使用了同一个 LoggingService 实例
如果我们从 LoggingService 中删除 @Injectable 并改为在 AppModule 中提供服务,它将如下所示:
import
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~