NestJS中使用DynamicModule构建插件系统
1. 介绍
在NestJS中,模块是组织代码的基本单元,它将相关的服务和控制器组织在一起。然而,在某些情况下,我们可能需要根据不同的条件动态加载模块,以满足不同的业务需求。这时,就可以使用DynamicModule了。
DynamicModule是NestJS提供的一种动态加载模块的方式,它允许我们在运行时动态地加载模块,下面就看看怎样通过DynamicModule构建插件系统。下面例子
- 创建了一个插件文件,其中定义了一些方法,用于动态加载;
- 创建了一个插件加载模块,用来加载上面的插件文件;
- 在主模块中,动态加载插件模块;
- 在主模块的controller中使用插件加载模块加载插件,并调用插件方法。
2. 创建插件文件
/tmp/test.js
module.exports = {
helloworld: function () {
console.log('load from dynamically loaded module!');
return 'Hello World';
},
hello: function (name) {
console.log('load from dynamically loaded module!');
return 'Hello ' + name;
},
};
3. 创建插件加载服务类
plugin-loader.service.ts
import { Inject, Injectable } from '@nestjs/common';
import * as path from 'path';
import * as vm from 'vm';
import * as fs from 'fs';
@Injectable()
export class PluginLoaderService {
constructor(@Inject('PLUGIN_FILE') private readonly pluginFile: string) {}
async loadPlugin(): Promise<any> {
console.log('load plugin ...', path.resolve(this.pluginFile));
const content = fs.readFileSync(path.resolve(this.pluginFile)).toString();
const sandbox = {
module: { exports: {} },
console: console,
};
vm.createContext(sandbox);
const script = new vm.Script(content, { filename: 'plugin-module.js' });
script.runInContext(sandbox);
return sandbox.module.exports;
}
}
- PluginLoaderService 用来根据传入的插件文件路径,使用vm加载之。
4. 创建插件加载模块
plugin.module.ts
import { DynamicModule, Module } from '@nestjs/common';
import { PluginLoaderService } from './plugin-loader.service';
@Module({})
export class PluginModule {
static loadPlugin(file: string): DynamicModule {
return {
module: PluginModule,
providers: [
{
provide: 'PLUGIN_FILE',
useValue: file,
},
PluginLoaderService,
],
exports: [PluginLoaderService],
};
}
}
- PluginModule 用来加载插件文件,并返回一个DynamicModule,其中包含PluginLoaderService。
- 在主模块中,可以通过调用PluginModule.loadPlugin(file)来动态加载插件文件。
5. 在主模块中使用插件加载模块
app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PluginModule } from './plugins/plugin.module';
import * as path from 'path';
@Module({
imports: [
PluginModule.loadPlugin('/tmp/test.js'),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
- 在主模块中,通过调用PluginModule.loadPlugin(file)来动态加载插件文件,并将其作为模块导入。
6. 在控制器中使用插件
app.controller.ts
import { Controller, Get, Inject } from '@nestjs/common';
import { AppService } from './app.service';
import { PluginLoaderService } from './plugins/plugin-loader.service';
@Controller()
export class AppController {
constructor(
private readonly appService: AppService,
private readonly pluginLoaderService: PluginLoaderService,
) {}
@Get()
async getHello() {
const plugin = await this.pluginLoaderService.loadPlugin();
console.log(plugin.helloworld());
console.log(plugin.hello('kongxx'));
return this.appService.getHello();
}
}
- 在控制器中,通过调用PluginLoaderService.loadPlugin()来加载插件,并调用插件的方法。