首页>>前端>>JavaScript->快速入门nest.js(6/10)

快速入门nest.js(6/10)

时间:2023-12-01 本站 点击:0

依赖注入

我们将依赖的实例委托给IOC容器,在这里,这个IOC容器就是NestJS运行时系统本身,NestJS在这里处理所有繁重的工作,而不是尝试自己实现依赖注入。本质上,当我们“ask”类构造函数中的依赖项时,NestJS处理并检索返回给我们的对象,以及它可能需要的任何依赖项等等。

比如,当我们使用CoffeeService并将其注入到我们的构造函数中时,NestJS发生了什么才能使所有这些工作。

export class CoffeesController {  constructor(private readonly coffeeService: CoffeesService){  }  // ...

在依赖注入过程中有3个关键步骤

首先,在我们的CoffeeService中,@Injectable装饰器声明了一个可以由Nest容器管理的类,此装饰器将CoffeeService类标记为Provider

@Injectable()export class CoffeesService {    // ...}

其次,如果我们进入CoffeesController,我们可以看到构造函数中正在请求CoffeesService,这个请求告诉Nest将提供程序inject到我们的控制器类中以便使用

export class CoffeesController {  constructor(private readonly coffeeService: CoffeesService){    // ...  }}

最后,Nest知道this类也是一个提供者,因为我们在CoffeeModule中包含了该提供者,,它向Nest IOC容器注册了这个提供者,这就是它在我们代码本身的运作方式。

@Module({// ...  providers: [CoffeesService],}

更加深入的理解:

当Nest容器实例化CoffeesController时,它首先查看是否由任何依赖项需要,在我们的例子中,有一个CoffeeService,当Nest容器找到CoffeesService依赖项时,它会对CoffeesService token执行查找,从而返回CoffeeService类,假设该Provider具有单例范围,这就是可注入提供程序的默认行为,然后,Nest将创建CoffeesService的实例,将其缓存并返回,或者已有缓存直接返回。<自下而上>

@Module({// ...  providers: [CoffeesService],})

其实是下面的缩略写法:

@Module({// ...  providers: [{      provide: CoffeesService,      useClass: CoffeesService,  }],})

在这个完整的写法中,我们可以清楚地将TOKEN CoffeesService与类CoffeesService相关联

控制module封装

默认情况下,NestJS模块封装了它们的提供者,这意味这无法注入不直接属于当前模块的提供者,也无法注入不是从导入模块导出的提供程序,因此你可以把从一个模块导出的提供者看作是该模块的公共接口。

// nest g mo coffee-rating// nest g s coffee-rating// 生成一个例子

假设我们的新CoffeeRatingService依赖CoffeesService从数据库中获取咖啡,而CoffeesService属于不同模块,所以我们需要在新的CoffeeRatingModule中导入CoffeesModule

使用基于构造函数的注入添加CoffeesService

// coffee-rating.serviceimport { Injectable } from '@nestjs/common';import { CoffeesService } from 'src/coffees/coffees.service';@Injectable()export class CoffeeRatingService {  constructor(private readonly coffeesService: CoffeesService){      }}

// coffee-rating.module@Module({  imports: [CoffeesModule],  providers: [CoffeeRatingService]})

此时如果直接运行会报依赖错误。

此时我们需要在CoffeesModule中将其添加到exports中:

// coffees.module@Module({// ...  exports:[CoffeesService],})

现在就没任何问题了...

深入CUSTOM PROVIDER

一些更为复杂的情况:

当我们正在创建我们的提供者的自定义实例,而不是让Nest为我们实例化该类;

假设我们想在第二个依赖项中重用现有类;

如果我们想用模拟版本覆盖一个类进行测试;

如果我们想使用策略模式,我们可以提供一个抽象类并根据不同的交换条件实际实现。

Nest允许我们自定义提供程序来处理这些用例

Value based Providers

useValue对于注入连续的值很有用,比如这里我们使用mock的数据MockCoffeesService

@Injectable()export class CoffeesService {    // ...}0

这时候,每当CoffeesService TOKEN被解析时,它将指向MockCoffeesService,任何时候偶们都会在我们的应用程序中注入CoffeesService

Nonclassbased Provider Tokens

有时我们可能希望灵活地使用字符串或符号作为依赖注入token

@Injectable()export class CoffeesService {    // ...}1

那么现在我们该如何使用呢?

我们需要使用类名声明依赖项,因为这就是Nest在IOC容器中查找依赖项的方式。

@Injectable()export class CoffeesService {    // ...}2

需要注意的是,虽然我们可以直接在内部使用COFFEES_BRANDS来注入装饰器,但最好在一个单独的文件定义Token,以便可以在整个应用程序中使用:

@Injectable()export class CoffeesService {    // ...}3

然后将@Inject('COFFEES_BRANDS') // 这里换成对应的变量COFFEES_BRANDS就可以了<最佳实践>

Class Provider

useclass允许我们动态确定一个Token应该解析到的Class

@Injectable()export class CoffeesService {    // ...}4

Factor Provider

useFactory允许我们动态创建提供者,如果提供者的值是基于各种其他依赖项值等。因为它们本身可以注入计算返回结果所需的其他提供程序。

@Injectable()export class CoffeesService {    // ...}5

Leverage Async Provider

有时我们需要引导程序延迟执行,直到一个或多个Asynchronous Tasks完成,我们需要做的就是async/awaituseFactory相结合。

@Injectable()export class CoffeesService {    // ...}6

动态模块

当我们有一个通用模块,该模块需要在不同情况下表现不同,将这个概念想象成一个博客系统,我们的动态模块需要一些配置才能被消费者使用。

这里出于演示,生成nest g mo database

@Injectable()export class CoffeesService {    // ...}7

由于我们对这些选项进行了硬编码,我们不能轻易地在不同地应用程序之间共享这个模块,如果另一个应用程序想要使用这个模块但它需要使用不同的端口怎么办?

这时候,我们就可以使用Nest的动态模块功能,我们可以让消费模块使用API来控制导入时自定义此DatabaseModule

@Injectable()export class CoffeesService {    // ...}8

控制Provider范围

NodeJS并不遵循请求/响应的多线程无状态模型,这种模型每个请求都由单独一个线程来处理。

因此,使用Singleton实例对我们的应用程序来说是完全安全的,但是在某些极端情况下,你可能需要提供者为某种所需行为提供基于请求的生命周期。

@INnjection()的作用域允许我们获得所需的提供者生命周期行为,默认情况下,NestJS中的每个提供者都是一个单例。

@Injectable()export class CoffeesService {    // ...}9

对于大多数用例,建议使用单例范围,其为最佳实践。

@Injectable提供者可用的另外两个生命周期:transentrequest-scoped

transent

transent提供者不会在消费者之间共享,注入瞬态提供者的每个消费者都将收到提供者的新专用实例

export class CoffeesController {  constructor(private readonly coffeeService: CoffeesService){    // ...  }}0

注意,该Service使用了两次,分别是在CoffeesController以及CoffeeBrandsFactory中,所以这里也实例化了两次。

如果我们删除{scope: Scope.TRANSIENT}会只实例化一次,该实例在CoffeesController以及CoffeeBrandsFactory中共享,这样会节约性能。

你也可以在Module里面像这样添加:

export class CoffeesController {  constructor(private readonly coffeeService: CoffeesService){    // ...  }}1

request-scoped

request-scoped会为每个到来的请求提供一个新的提供者实例,当然,在请求完成处理后,也会对实例进行垃圾收集

export class CoffeesController {  constructor(private readonly coffeeService: CoffeesService){    // ...  }}2

初始时,没有进行任何实例化,后面进行了三次请求:

意味着该服务是为每个请求创建的。

CoffeesController是一个单例,为什么CoffeesService会被创建3次,我们并没有在控制器装饰器中修改任何东西?

其实在Nest中,这些scope会向上的注入链冒泡,这意味着如果CoffeesController依赖于属于REQUSET范围的CoffeesService,它也会隐式地变成REQUSET范围

我们在其中添加log:

export class CoffeesController {  constructor(private readonly coffeeService: CoffeesService){    // ...  }}3

然后请求三次:

这对于你访问请求特定信息,非常有用,例如标头、cookie、IP等

显示的使用:

export class CoffeesController {  constructor(private readonly coffeeService: CoffeesService){    // ...  }}4

\

原文:https://juejin.cn/post/7097941612524732429


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/JavaScript/6411.html