Javascript 如何为用typescript编写的模块编写插件加载程序
我正在处理一个模块,作为启动的一部分,它将根据配置文件动态加载其他模块,配置文件提供要导入的包或文件的名称。我用vanilla JS写了一些不错的东西,但我正在努力将它翻译成打字脚本 在Javascript中,我正在做: ./src/main.js(假设Javascript 如何为用typescript编写的模块编写插件加载程序,javascript,typescript,Javascript,Typescript,我正在处理一个模块,作为启动的一部分,它将根据配置文件动态加载其他模块,配置文件提供要导入的包或文件的名称。我用vanilla JS写了一些不错的东西,但我正在努力将它翻译成打字脚本 在Javascript中,我正在做: ./src/main.js(假设config.plugins=['./plugin']): ./src/plugin.js: class Plugin() { } module.exports = (main_module) => { main_module.plu
config.plugins=['./plugin']
):
./src/plugin.js:
class Plugin() {
}
module.exports = (main_module) => {
main_module.plugin = new Plugin();
}
尝试将其转换为Typescript让我明白了(主要的区别是插件现在是异步加载的,这是一个问题,但至少可以编译,所以这里不是我的重点):
./src/main.ts:
interface Config {
plugins: string[];
}
interface Plugin {
register(main_module: ModuleName): void;
}
export class ModuleName {
public config: Config;
public constructor(config: Config) {
this.config = config;
}
public async init(): Promise<string[]> {
return this.loadPlugins(this.config.plugins);
}
// You don't need to return anything from here because `async` functions
// always return a `Promise`.
private async loadPlugins(plugins: string[]): Promise<string[]> {
for (let plugin of plugins) {
// Here using `async`/`await` is a lot less complicated than using
// `then()` because of the `for...of` loop
let loaded: Plugin = await import(plugin);
loaded.register(this);
}
return plugins;
}
}
现在,在main.ts中,它允许我自由引用这个.plugin
。但是,当我尝试将其导入到项目中时,我不会引用该插件
例如,将上述内容编译到/dist
文件夹,并具有以下内容(在我的项目根目录下):
/test.ts
import { ModuleName } from './dist';
let a = new ModuleName({plugins: ['./plugin']});
a.init().then(a.plugin.foo()).catch(console.error);
tsc指出类型“ModuleName”上不存在属性“plugin”时出错。看起来您正在异步加载它,并且可能试图过快地访问它。使用
setTimeout
来确保在需要时完成异步进程绝对不是一个好主意(如果进程在速度快的计算机上需要50毫秒,但在速度慢的计算机上需要500毫秒怎么办?);相反,您希望直接访问Promise
,以便可以使用Promise.then()
。问题是,您永远不希望从构造函数返回对象本身以外的任何内容,因此您永远不希望使用异步构造函数。更好的模式是使用单独的初始化函数来执行异步过程
此外,import(plugin)
返回一个Promise
,而不是插件本身。因此,您还必须相应地处理此问题,并等待解析由loadPlugins
返回的Promise
,直到所有import
s解析完成。这可能不是一个很容易阅读的语句,因此这里有一些代码可以比我更好地解释它:
export class ModuleName {
private config: Config;
public constructor(config: Config) {
this.config = config;
}
public async init(): Promise<void> {
return this.loadPlugins(this.config.plugins);
}
// You don't need to return anything from here because `async` functions
// always return a `Promise`.
private async loadPlugins(plugins: string[]): Promise<void> {
for (let plugin of plugins) {
// Here using `async`/`await` is a lot less complicated than using
// `then()` because of the `for...of` loop
let loaded: Plugin = await import(plugin);
loaded.register(this);
}
}
}
或者,即使这样也能奏效:
设a=newmodulename(配置);
a、 init().then(a.plugin.foo).catch(console.error)
虽然这是一种更好的异步加载插件的方法,但这并不能解决更大的问题,即在导入模块时,由于TS抛出错误,我仍然无法引用任何插件。如果您按照我这里的方式更新main.TS
,它应该可以工作(至少对我有效)。是否将配置对象传递给newmoduleName()代码>?在plugin.ts
中,您有main\u module:module
,但它应该是main\u ModuleName:ModuleName
。这是一个输入错误。我已经使用您对async的建议更新了我的问题,并显示了my main.ts的外观/位置以及它是如何引用模块的。就像我说的,如果我把上面的类instantation放在./src/index.ts中,它可以工作,但是放在./test.ts(它引用模块的编译版本)中,没有骰子。此外,这只是假设插件是从与index.ts相同的文件夹加载的。当插件从say@name/module-plugin1加载时会发生什么?啊,我想我明白了。谢谢您的帮助。您需要导出插件
import { ModuleName } from './dist';
let a = new ModuleName({plugins: ['./plugin']});
a.init().then(a.plugin.foo()).catch(console.error);
export class ModuleName {
private config: Config;
public constructor(config: Config) {
this.config = config;
}
public async init(): Promise<void> {
return this.loadPlugins(this.config.plugins);
}
// You don't need to return anything from here because `async` functions
// always return a `Promise`.
private async loadPlugins(plugins: string[]): Promise<void> {
for (let plugin of plugins) {
// Here using `async`/`await` is a lot less complicated than using
// `then()` because of the `for...of` loop
let loaded: Plugin = await import(plugin);
loaded.register(this);
}
}
}