新建 项目看看
nest new dynamic-module -p npm
新增一个模块
nest g resource bbb
此时是固定静态引入
传一些参数,动态生成模块的内容,能行吗?
使用 DynamicModule
import { DynamicModule, Module } from '@nestjs/common';
import { BbbService } from './bbb.service';
import { BbbController } from './bbb.controller';
@Module({})
export class BbbModule {
static register(options: Record<string, any>): DynamicModule {
return {
module: BbbModule,
controllers: [BbbController],
providers: [
{
provide: 'CONFIG_OPTIONS',
useValue: options,
},
BbbService,
],
exports: [],
};
}
}
使用时 通过 register 方法传入参数,返回值就是模块定义
这样我们就可以在 import 一个模块的时候,传入参数,然后动态生成模块的内容。
这就是 Dynamic Module
est 约定了 3 种方法名:
-
register
-
forRoot
-
forFeature
-
register:用一次模块传一次配置,比如这次调用是 BbbModule.register({aaa: 'aaaValue',}),下一次就是 BbbModule.register({ bbb: 'bbbValue',})
-
forRoot:配置一次模块用多次,比如 XxxModule.forRoot({}) 一次,之后就一直用这个 Module,一般在 AppModule 里 import
-
forFeature:用了 forRoot 固定了整体模块,用于局部的时候,可能需要再传一些配置,比如用 forRoot 指定了数据库链接信息,再用 forFeature 指定某个模块访问哪个数据库和表
forRoot:全局配置一次,用无数次
典型使用场景
- 数据库
- Redis
- 配置中心
- Logger
👉 整个应用只需要一份配置
DatabaseModule.forRoot()
1️⃣ 定义模块
// database.module.ts
import { DynamicModule, Global, Module } from '@nestjs/common';
export const DB_CONFIG = 'DB_CONFIG';
@Global() // 可选,但非常常见
@Module({})
export class DatabaseModule {
static forRoot(config: { host: string; port: number }): DynamicModule {
return {
module: DatabaseModule,
providers: [
{
provide: DB_CONFIG,
useValue: config,
},
],
exports: [DB_CONFIG],
};
}
}
2️⃣ 在 AppModule 里只用一次
@Module({
imports: [
DatabaseModule.forRoot({
host: 'localhost',
port: 3306,
}),
],
})
export class AppModule {}
3️⃣ 任意地方直接注入
@Injectable()
export class UserService {
constructor(
@Inject(DB_CONFIG) private dbConfig: any,
) {}
}
forRoot 的一句总结
“全局只配一次,整个系统共用”
forFeature:在 forRoot 的基础上,再做局部扩展
典型组合
XxxModule.forRoot(...) // 全局基础设施
XxxModule.forFeature(...) // 业务模块细化配置
数据库 + 表模型
1️⃣ forRoot:建立数据库连接
// database.module.ts
@Module({})
export class DatabaseModule {
static forRoot(config: DbConfig): DynamicModule {
return {
module: DatabaseModule,
providers: [
{
provide: 'DB_CONNECTION',
useValue: createConnection(config),
},
],
exports: ['DB_CONNECTION'],
};
}
}
2️⃣ forFeature:声明“我这个模块用哪些表”
static forFeature(entities: any[]): DynamicModule {
return {
module: DatabaseModule,
providers: entities.map(entity => ({
provide: `REPO_${entity.name}`,
useFactory: (conn) => conn.getRepository(entity),
inject: ['DB_CONNECTION'],
})),
exports: entities.map(e => `REPO_${e.name}`),
};
}
3️⃣ 使用方式(Nest 项目常见)
@Module({
imports: [
DatabaseModule.forRoot({
host: 'localhost',
port: 3306,
}),
DatabaseModule.forFeature([UserEntity, OrderEntity]),
],
})
export class UserModule {}
4️⃣ 注入具体 Repository
@Injectable()
export class UserService {
constructor(
@Inject('REPO_UserEntity') private userRepo: any,
) {}
}
forFeature 的一句总结
“在全局能力之上,为某个业务模块补充专属配置”