深入解析 lowcode-engine 插件实例模型 PluginInstance:属性、依赖与元数据机制
2026/9/14 10:43:19 网站建设 项目流程

深入解析 lowcode-engine 插件实例模型 PluginInstance:属性、依赖与元数据机制

【免费下载链接】lowcode-engineAn enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系项目地址: https://gitcode.com/GitHub_Trending/lo/lowcode-engine

插件体系是 lowcode-engine 低代码引擎的核心扩展机制,而PluginInstance(插件实例)则是描述"一个已注册插件在运行时状态"的标准模型。本文以 docs/docs/api/model/plugin-instance.md 中的模型定义为主线,结合仓库内类型定义与 Shell 层实现,系统讲解pluginNamedepdisabledmeta四个属性的语义、取值来源与典型使用场景,并深入IPublicTypePluginMeta元数据背后的依赖声明、引擎版本兼容、事件前缀与命令作用域等机制。读完本文,你将能在自己的插件开发与宿主集成中准确读写插件实例,实现依赖编排、启停控制和元数据读取。

一、什么是插件实例 PluginInstance

在 lowcode-engine 的架构中,插件(Plugin)通过plugins.register()注册到插件管理器,注册后由插件管理器实例化出对应的运行时对象(ILowCodePluginRuntime)。PluginInstance 是面向外部使用者的只读/受限访问模型,它把运行时内部细节收敛为四个稳定的公开属性,屏蔽底层实现差异。

  • 对应的公开类型为IPublicModelPluginInstance,定义于 packages/types/src/shell/model/plugin-instance.ts;
  • 该模型自引擎v1.1.0起提供(@since v1.1.0);
  • Shell 层的具体实现类为PluginInstance,位于 packages/shell/src/model/plugin-instance.ts,它内部持有运行时对象ILowCodePluginRuntime,并以pluginInstanceSymbol作为私有字段的隐藏键。
// packages/types/src/shell/model/plugin-instance.ts(节选) export interface IPublicModelPluginInstance { disabled: boolean; get pluginName(): string; get dep(): string[]; get meta(): IPublicTypePluginMeta; }

从上述接口可以看到,pluginNamedepmeta是只读 getter,而disabled是唯一可读写的属性——这正是"实例状态"语义的体现:插件身份与依赖不可变,但启用/禁用状态可在运行期调整。

二、如何获取插件实例

PluginInstance 通常不是由开发者直接new出来的,而是通过插件管理 API 从插件管理器中取得。在 Shell 层,plugins.get()plugins.getAll()会把底层运行时对象包装为公开的插件实例模型:

// packages/shell/src/api/plugins.ts(节选) get(pluginName: string): IPublicModelPluginInstance | null { const instance = this[pluginsSymbol].get(pluginName); if (instance) { return new ShellPluginInstance(instance); } return null; } getAll(): IPublicModelPluginInstance[] { return this[pluginsSymbol].getAll()?.map((d) => new ShellPluginInstance(d)); }

典型用法:

// 通过插件上下文或编辑器拿到 plugins API const plugins = editor.get('plugins'); // 按名称获取单个插件实例 const outlinePane = plugins.get('PluginOutlinePane'); if (outlinePane) { console.log(outlinePane.pluginName); // 'PluginOutlinePane' console.log(outlinePane.disabled); // false } // 获取全部插件实例 const all = plugins.getAll(); all.forEach((p) => console.log(p.pluginName, p.dep, p.meta));

注意:get()在插件不存在时返回null,使用前应做空值判断。

三、核心属性详解

3.1 pluginName:插件名字

类型string

pluginName是插件的唯一标识名,注册插件时定义(即IPublicTypePlugin上的pluginName字段),运行期不可修改。在 Shell 实现中它直接透传运行时对象的name

// packages/shell/src/model/plugin-instance.ts get pluginName(): string { return this[pluginInstanceSymbol].name; }

它在整个插件体系中承担多重职责:

  • 作为plugins.get(pluginName)plugins.has(pluginName)等 API 的查找键;
  • 作为插件上下文的事件命名空间来源之一(见下文meta.eventPrefix的推荐用法);
  • 作为插件间依赖声明(dep)的引用目标。

3.2 dep:插件依赖

类型string[]

dep表示当前插件所依赖的其他插件名称列表。lowcode-engine 插件管理器在初始化时会依据各插件的dep进行依赖排序(拓扑初始化),确保被依赖的插件先完成初始化,再初始化依赖方,从而避免"使用尚未就绪的插件"的问题。

// packages/shell/src/model/plugin-instance.ts get dep(): string[] { return this[pluginInstanceSymbol].dep; }

声明依赖的两种途径:

  1. 在插件meta.dependencies中声明(推荐,静态元数据);
  2. 在注册时通过dep传入依赖列表(运行时声明)。

从源码结构看,depmeta.dependencies存在对应关系,二者共同构成插件管理器的依赖解析输入。例如某个面板插件依赖大纲树插件时:

const MyPlugin = (ctx) => { // 依赖的插件已保证先初始化,可安全使用 const outline = ctx.plugins.get('PluginOutlinePane'); return { init() { /* ... */ } }; }; MyPlugin.pluginName = 'MyPlugin'; MyPlugin.meta = { dependencies: ['PluginOutlinePane'], };

3.3 disabled:插件是否禁用

类型boolean

disabled表示插件实例当前是否被禁用。它是插件实例模型中唯一可写的属性,读写均受 Shell 层支持:

// packages/shell/src/model/plugin-instance.ts get disabled(): boolean { return this[pluginInstanceSymbol].disabled; } set disabled(disabled: boolean) { this[pluginInstanceSymbol].setDisabled(disabled); }
  • 读取时透传运行时对象的disabled字段;
  • 写入时会调用运行时对象的setDisabled(disabled),即修改状态是"有副作用的操作",会真正影响插件的启用/停用逻辑,而不是简单改一个标志位。

与之等价的管理器级 API 是plugins.setDisabled(pluginName, flag)(见 packages/designer/src/plugin/plugin-types.ts 中ILowCodePluginManagerCore.setDisabled)。典型场景:按用户权限或项目配置动态禁用某些内置面板。

3.4 meta:插件 meta 信息

类型IPublicTypePluginMeta

meta承载插件的配置元数据,是插件体系中最有信息量的属性,其完整定义位于 packages/types/src/shell/type/plugin-meta.ts。Shell 实现中直接返回运行时对象的meta

// packages/shell/src/model/plugin-instance.ts get meta() { return this[pluginInstanceSymbol].meta; }

四、IPublicTypePluginMeta 元数据逐项解析

IPublicTypePluginMeta定义了五个可选字段,下面结合源码注释逐一说明。

4.1 dependencies:插件依赖声明

/** * define dependencies which the plugin depends on */ dependencies?: string[];

以数组形式声明本插件依赖的其他插件名。插件管理器据此进行依赖拓扑排序,保证初始化顺序正确。dependenciesdep属性的元数据来源之一。

4.2 engines:引擎版本兼容声明

engines?: { /** e.g. '^1.0.0' */ lowcodeEngine?: string; };

声明插件兼容的引擎版本范围,采用 npm 语义化版本(semver)规则,例如'^1.0.0'表示兼容 1.x 系列。引擎在初始化插件时可据此做版本校验,避免插件与引擎版本不匹配导致的运行异常。

4.3 preferenceDeclaration:偏好配置声明

preferenceDeclaration?: IPublicTypePluginDeclaration;

声明插件的偏好配置项(preference),用于在插件设置面板中展示可配置项。IPublicTypePluginDeclaration定义于 packages/types/src/shell/type/plugin-declaration.ts。声明后,插件可通过上下文读取用户配置的偏好值。

4.4 eventPrefix:事件前缀

这是元数据中行为影响最直接的字段。源码注释给出了清晰的规则:

/** * use 'common' as event prefix when eventPrefix is not set. * strongly recommend using pluginName as eventPrefix * * eg. * case 1, when eventPrefix is not specified * event.emit('someEventName') is actually sending event with name 'common:someEventName' * * case 2, when eventPrefix is 'myEvent' * event.emit('someEventName') is actually sending event with name 'myEvent:someEventName' */ eventPrefix?: string;

要点总结:

  • 未设置eventPrefix,插件通过event.emit('someEventName')发出的事件,实际事件名会被加上common:前缀,即common:someEventName
  • 设置eventPrefix: 'myEvent',实际事件名为myEvent:someEventName
  • 官方强烈建议使用插件名(pluginName)作为eventPrefix,以避免不同插件的事件在common前缀下互相冲突,实现事件隔离。

4.5 commandScope:命令作用域

/** * 如果要使用 command 注册命令,需要在插件 meta 中定义 commandScope */ commandScope?: string;

如果插件要通过 command 能力注册命令,则必须在meta中定义commandScope。该字段规定了插件注册的命令所属的作用域,便于命令的统一管理与冲突规避。

4.6 一个完整的 meta 示例

const DemoPlugin = (ctx) => ({ init() { // 事件名实际为 'DemoPlugin:hello' ctx.event.emit('hello', { from: 'demo' }); }, exports() { return { answer: 42 }; }, }); DemoPlugin.pluginName = 'DemoPlugin'; DemoPlugin.meta = { dependencies: ['PluginOutlinePane'], engines: { lowcodeEngine: '^1.0.0' }, eventPrefix: 'DemoPlugin', // 推荐使用 pluginName 作为事件前缀 commandScope: 'demo', }; DemoPlugin.preferenceDeclaration = { title: 'Demo 插件偏好', properties: [ { key: 'showTip', type: 'boolean', title: '是否显示提示', default: true, }, ], };

五、底层运行机制:从 PluginInstance 到 ILowCodePluginRuntime

IPublicModelPluginInstance是一个"门面",真正的运行时逻辑由ILowCodePluginRuntime承担。该运行时类型定义于 packages/designer/src/plugin/plugin-types.ts:

export interface ILowCodePluginRuntimeCore { name: string; dep: string[]; disabled: boolean; config: IPublicTypePluginConfig; logger: IPublicApiLogger; meta: IPublicTypePluginMeta; init(forceInit?: boolean): void; isInited(): boolean; destroy(): void; toProxy(): any; setDisabled(flag: boolean): void; }

从源码结构可以看到几个关键点:

  • 运行时对象拥有init(forceInit)destroy()isInited()等生命周期方法,而公开模型刻意不暴露这些内部方法,保持对外 API 的克制与稳定;
  • disabled的写入最终落到setDisabled(flag),说明禁用是一个可恢复的状态切换;
  • 运行时通过IPublicTypePluginConfig(定义于 packages/types/src/shell/type/plugin-config.ts)描述插件的initdestroyexports三个生命周期钩子:
    • init(): Promise<void> | void:插件初始化入口;
    • destroy?(): Promise<void> | void:插件销毁清理;
    • exports?(): any:对外暴露的公共 API,可通过插件实例访问。

因此,当你通过plugins.get(name)拿到IPublicModelPluginInstance时,实际看到的是运行时状态的一层稳定投影:身份(pluginName)、依赖(dep)、状态(disabled)与元数据(meta)

六、实战:综合使用插件实例模型

结合以上内容,给出一个完整的实战片段:读取、校验并控制插件实例。

import { IPublicModelPluginInstance } from '@alilc/lowcode-types'; function inspectPlugin(instance: IPublicModelPluginInstance | null): void { if (!instance) { console.warn('插件不存在'); return; } // 1. 身份信息 console.log('插件名:', instance.pluginName); // 2. 依赖信息:打印依赖的其他插件 console.log('依赖插件:', instance.dep?.join(', ') || '(无)'); // 3. 状态读写:禁用 / 恢复 if (!instance.disabled) { instance.disabled = true; // 等价于 plugins.setDisabled(name, true) console.log(`${instance.pluginName} 已禁用`); instance.disabled = false; // 恢复启用 } // 4. 元数据:依赖、引擎版本与事件前缀 const meta = instance.meta; console.log('声明的依赖:', meta.dependencies); console.log('兼容引擎版本:', meta.engines?.lowcodeEngine); console.log('事件前缀:', meta.eventPrefix ?? 'common'); console.log('命令作用域:', meta.commandScope); } // 使用 const plugin = editor.get('plugins').get('DemoPlugin'); inspectPlugin(plugin);

使用建议:

  • 读取优先、写入谨慎pluginNamedepmeta为只读,唯一可写的disabled会触发真实的启停逻辑,改动前请确认业务时序;
  • 善用depmeta.dependencies:依赖声明是插件管理器保证初始化顺序的依据,插件间协作务必显式声明;
  • 事件前缀用插件名:遵循源码注释中"strongly recommend using pluginName as eventPrefix"的建议,避免common:命名空间下的跨插件事件污染。

七、小结

PluginInstance 模型以四个精简属性(pluginNamedepdisabledmeta)完整刻画了一个插件实例的身份、依赖、状态与元数据。它既是插件管理 API(packages/shell/src/api/plugins.ts)对外返回的统一视图,也是底层运行时(packages/designer/src/plugin/plugin-types.ts)的安全门面。理解这一模型,是编写高质量 lowcode-engine 插件、进行插件间依赖编排与动态启停控制的基础。进一步阅读可参考 插件注册与生命周期 以及插件实例相关的模型 API 文档,并结合 plugin-meta 类型定义 验证各元数据字段的实际约束。

【免费下载链接】lowcode-engineAn enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系项目地址: https://gitcode.com/GitHub_Trending/lo/lowcode-engine

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询