【问题标题】:Typescript dynamic module loading at runtimeTypescript 在运行时动态加载模块
【发布时间】:2018-11-05 03:10:05
【问题描述】:

我在一个 NodeJS Typescript 项目中定义了一个抽象 BaseClass,并且我有一个实现和扩展这个 BaseClass 的派生类列表。

// baseModule.ts
export abstract class BaseClass {
  constructor() {}
  abstract method(): void;
}

export interface ModuleConstructor<T extends BaseClass> {
  new (): T
}

export function createModule<T extends BaseClass>(type: ModuleConstructor<T>): T {
  return new type();
}

我正在尝试找到一种在运行时以编程方式创建这些类之一的实例的方法。

这里的限制是我希望能够将一个新的myDerivedClass.ts 文件放到我的项目文件夹中,并让它在运行时自动包含在可用模块列表中。

开发人员的工作流程是 1) 创建新文件 myNewModule.ts 2) 创建并导出一个扩展 BaseClass 的类 3) 将 myNewModule.ts 保存到 ./myModules

// ./myModules/myNewModule.ts
export class MyModule extends BaseClass {
  constructor() {
    super()
  }

  method() {
    //Do something custom
  }
}

运行时流程(理想情况下无需重新构建)将是 1) 用户从可用模块列表中选择 2) createModule 工厂函数创建所选模块的新实例并作为

的实例传递
// someOtherClass.ts

const modules = require('./myModules/*') //<- Something to this effect
import { BaseClass, createModule, ModuleConstructor } from './BaseClass'

export class SomeOtherClass {
  public mod: BaseClass
  constructor(mod: ModuleConstructor) {
    this.mod = createModule(mod)
  }
}

for (let m in modules) {
  console.log(modules[m].name);
}

let someObj = SomeOtherClass(modules[m]);
someObj.mod // <- instance of derived class.

【问题讨论】:

    标签: node.js typescript es6-modules dynamic-loading


    【解决方案1】:

    这是我最终使用的解决方案,但也许有更简单的方法。

    1) 创建动态模块加载器函数 2)使用NodeJSfs模块扫描包含模块的目录 3) 遍历每个文件并将文件动态导入到数组中

    // BaseModule.ts
    import * as path from 'path'
    import { promisify } from 'utils'
    
    const readdirAsync = promisify(fs.readdir);
    
    export async function loadModules() {
      let files = await readdirAsync(path.resolve(__dirname, 'rel/path/to/modules'));
      let imports = await Promise.all(files.map(file => (
        import(path.resolve(__dirname, '..', './exchanges/brokers', file))))
      )
    
      // this next part will depend on how you're exporting within
      // the module. In my case, each module has an "export class"
      // statement. Typically you would "import { className } from 'moduleName'"
    
      let moduleNames: { [name: string]: number } = {};
      let modules: ModuleConstructor<BaseClass>[] = [];
      for (let i in imports) {
        Object.keys(imports[i]).forEach((key: string) => {
          moduleNames[key] = modules.length;
          modules.push(imports[i][key])
        })
      }
      return [moduleNames, modules];
    }
    
    
    // someOtherClass.ts
    import { BaseClass, loadModules, ModuleConstructor } from './BaseClass'
    
    export class SomeOtherClass<T extends BaseClass> {
      public mod: T
      constructor(mod: T ) {
        this.mod = mod;
      }
    }
    
    loadModules()
      .then(([moduleNames, modules]: [string[], ModuleConstructor<BaseClass>[]] => {
    
        // not necessary, but just for purpose of demonstration
        let names: string[] = Object.keys(moduleNames);
        let name = names[0]; // pick one of the module names
    
        // use the name dictionary as a lookup into the constructor array
        let someObj = SomeOtherClass(new modules[moduleNames[name]]);
        someObj.mod // <- instance of derived class.
      })
      .catch((err) => {
        console.log(err.message);
      });
    

    这样做之后,我意识到 NPM 包 systemJS 处理动态加载器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-08-22
      • 2017-10-11
      • 2018-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-19
      相关资源
      最近更新 更多