跳到主要内容

NestJS-进阶玩法

Provider

Nest中主要有几种种定义Provider的方法

  • TypePrivider

  • ClassProvider

  • ValueProvider

  • FactoryProvider

  • ExistingProvider

我们分别介绍一下他们的含义和具体使用方法,而除了TypeProvider之外都会有一个provider属性,在需要注入的地方可以通过@Inject(provide)来使用。而其中的provider属性可以是string | symbol | Type<any> | Abstract<any> | Function

TypeProvider

TypeProvider就是最常用的,Nest中将一个类加上@Injectable()注解,然后在所属的模块中将类providers数组中。这时就可以在需要的地方直接注入即可使用。

ClassProvider

ClassProvider通过属性useClass指定一个特定的类型。在需要的地方通过@Inject()它的provider熟悉就可以拿到一个具体的实例,这是因为Nest框架内部有一套实例化的逻辑。

{ provide: BookService, useClass: BookService }

ValueProvider

ValueProvider通过属性useValue来指定一个实例作为Provider。比较特殊的是ValueProvider不能额外指定scope,因为它永远是单例的。

const app = { name: 'Nest-first-instance1', createdAt: '2023-02-07' };
// 将下面的代码加入到 providers数组中
{ provide: 'app', useValue: app }

FactoryProvider

我们可以通过useFactory动态地创建Provider。实际的Provider将由工厂函数返回的值提供。工厂函数可以根据需要自己添加任意的逻辑。一个简单的工厂可以不依赖于任何其它Provider。一个更复杂的工厂可以自己注入它所需要的其他Provider来计算其结果。对于后一种情况,useFactory可以接受一组参数,这些参数必须是其他可以注入的Provider

{
provide: 'factory',
inject: [{ token: 'app', optional: false }],
useFactory: (app: App) => {
return {
name: 'factory',
createdAt: new Date
};
},
},

useFactory也可以接收一个异步函数

{
provide: 'async_factory',
useFactory: async () => {
return new Promise<App>(function (resolve) {
setTimeout(() => {
resolve({
name: app.name,
createdAt: new Date(),
});
}, Math.random() * 1000);
});
},
},

ExistingProvider

为已经存在的Provider创建别名,下面的代码中BookService"exists_book"都是同样的Provider

{ provide: BookService, useClass: BookService },
{
provide: 'exists_book',
useExisting: BookService,
}

所有的Provider都可以指定它的scope,通常Provider有三种scope

  • default(单例模式):永远都是同一个实例;

  • REQUEST:每个请求都会有对应的Provider实例创建,在请求完成后实例被销毁;

  • TRANSIENT:每次都能拿到新的实例;

指定方式:

import { Injectable, Scope } from '@nestjs/common';

@Injectable({ scope: Scope.REQUEST })
export class BookService {}

// 或者在module的providers中定义
{ provide: BookService, useClass: BookService, scope: Scope.REQUEST },
{
provide: 'exists_book',
useExisting: BookService,
scope: Scope.REQUEST
}

Module

模块的常规操作已经在概念中了解过了,使用方法通常是直接 这里说说动态模块。所谓的动态模块其实就是根据