nestjs自动注册模块,再也不用频繁在AppModule里import了

234 阅读2分钟

在nest中,当我们创建一个module, 需要在AppModule注册才能使用。

src
 ├── app.controller.spec.ts
 ├── app.controller.ts
 ├── app.module.ts
 ├── app.service.ts
 ├── main.ts
 └── modules
      ├── user
      └── menu
// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import UserModule from './modules/user/user.module';
import MenuModule from './modules/menu/menu.module';

@Module({
  imports: [
    UserModule, // 注册UserModule
    MenuModule, // 注册MenuModule
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

这样在module多了或者层级复杂的情况下,每次都要手动注册,非常繁琐。

而nest是支持动态导入module的。因此我们可以写一个module的注册工具类,它会将对应目录下的所有module自动注册

// auto-register.util.ts
// 导入path模块的所有内容
import * as path from 'path';
// 导入fs模块的所有内容
import * as fs from 'fs';
// 从@nestjs/common导入DynamicModule, Global, Module
import { DynamicModule, Module, Type } from '@nestjs/common';

// 定义一个接口AutoRegisterOptions,包含一个path属性
export interface AutoRegisterOptions {
  path: string;
}

// 定义一个类, 并且@Module装饰器设置为module
@Module({})
export class AutoRegisterModule {
  // 静态方法registerModules,接收一个AutoRegisterOptions对象,返回一个DynamicModule对象
  static registerModules(options: AutoRegisterOptions): DynamicModule {
    // 调用loadModules方法获取模块数组
    const modules = this.loadModules(options.path);
    // 返回一个包含module和imports属性的对象
    return {
      module: AutoRegisterModule,
      imports: modules,
    };
  }

  // 私有静态方法loadModules,接收一个目录路径,返回模块数组
  private static loadModules(directory: string): Type<any>[] {
    // 将当前目录与传入目录拼接成完整路径
    const normalizedPath = path.join(__dirname, directory);
    // 调用loadModulesRecursively方法递归加载模块
    return this.loadModulesRecursively(normalizedPath);
  }

  // 私有静态方法loadModulesRecursively,递归加载模块
  private static loadModulesRecursively(directory: string): any[] {
    // 读取目录中的文件
    const files = fs.readdirSync(directory);

    // 使用reduce方法遍历文件,并累加模块数组
    return files.reduce((importedModules: Type<any>[], file) => {
      // 拼接文件的完整路径
      const filePath = path.join(directory, file);
      // 检查文件是否是目录
      const isDirectory = fs.statSync(filePath).isDirectory();
      if (isDirectory) {
        // 如果是目录,则递归加载子目录中的模块
        importedModules.push(...this.loadModulesRecursively(filePath));
      } else if (file.endsWith('.module.js')) {
        // 如果是以.module.js结尾的文件,则加载该文件
        // eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
        const module = require(filePath);
        // 如果模块存在且有一个默认导出,则将该默认导出添加到模块数组中
        if (module && module.default) {
          importedModules.push(module.default);
        } else {
          // 如果没有默认导出,则输出错误信息
          console.error(
            `Module at ${filePath} does not have a default export.`,
          );
        }
      }

      // 返回累加后的模块数组
      return importedModules;
    }, []);
  }
}

然后在AppModule里注册AutoRegisterModule

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AutoRegisterModule } from './utils/auto-register.util';

@Module({
  imports: [
    AutoRegisterModule.registerModules({
      path: '../modules',
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

重启项目,就会发现所有module都被自动注册了。

注意该方法需要module是使用export default导出的