使用 Dynamic Module

很多情况下,我们希望在引入模块时传一些参数,动态生成模块的内容。

这就用到 Dynamic Module 了。

示例

使用 cli 创建一个 module。

bash
1nest generate module MyFirst --no-spec
2
3CREATE src/my-first/my-first.module.ts (84 bytes)
4UPDATE src/app.module.ts (322 bytes)

生成后的的代码是这样的:

typescript
1import { Module } from '@nestjs/common'
2
3@Module({})
4export class MyFirstModule {}

一个是 @Module 装饰器,还一个是 Module 的 class。

实现动态 Module 的方法就是实现一个 register 方法:

typescript
1export class MyFirstModule {
2  static register(options: Record<string, any>): DynamicModule {
3    const optionProvider = {
4      provide: 'CONFIG_OPTIONS',
5      useValue: options,
6    }
7    return {
8      module: MyFirstModule,
9      providers: [optionProvider],
10      exports: [optionProvider],
11    }
12  }
13}

这个动态 Module 的 provider 是返回一个注册 Module 时传入的 options 对象。

接着在需要引入的 Module 中调用 register方法:

typescript
1import { MyFirstModule } from './my-first/my-first.module'
2
3@Module({
4  imports: [MyFirstModule.register({ name: 'Dynamic Module' })],
5  controllers: [AppController],
6  providers: [AppService],
7})
8export class AppModule {}

在引入动态 Module 时,传入{ name: 'Dynamic Module' },它内部的 CONFIG_OPTIONS provider 也就有了固定值。

现在在 AppService 中,已经可以使用 MyFirstModule 的 token 为 “CONFIG_OPTIONS” 的 Provider 了。

typescript
1import { Inject, Injectable } from '@nestjs/common'
2
3@Injectable()
4export class AppService {
5  constructor(@Inject('CONFIG_OPTIONS') private readonly options: Record<string, any>) {}
6
7  getHello(): string {
8    console.log(this.options)
9    return 'Hello World!'
10  }
11}

通过访问http://localhost:3000/,你将在终端看到结果:

image-20240120220810167

动态 Module 的用法大致如此。

方法名

register 方法并非强制规定,想叫啥都是可以的,只要保证在引入时调用这个方法即可。

但为了规范性,nest 约定了三个方法名:

  • register:用一次模块传一次配置,比如这次调用是 BbbModule.register({aaa:1}),下一次就是 BbbModule.register({aaa:2}) 了。
  • forRoot:配置一次模块用多次,一般出现在 AppModule 里。
  • forFeature:用了 forRoot 固定了整体模块,用于局部的时候,可能需要再传一些配置,比如用 forRoot 指定了数据库链接信息,再用 forFeature 指定某个模块访问哪个数据库和表。

总结

Module 可以传入 options 动态产生,这叫做动态 Module。

动态 Module 通过在 Module class 中定义 static 方法来实现。

建议的 Module 的方法名有 register、forRoot、forFeature 3 种。

这些方法还可以写成 xxxAsync 版本,也就是传入 useFactory 等 option,内部注册异步 provider。

在后续的数据库、ORM 接入时,会大量使用动态 Module。

代码示例