替换内置插件实战指南
替换内置插件实战指南
本指南将通过实际案例,教你如何从零开发自定义插件、替换内置插件,以及实现高度定制的引擎。
CPlugin 类型定义
在开始之前,先了解 CPlugin 的类型定义:
// 插件对象定义export type PluginObj<C, E = any> = { name: string; // 插件唯一名称(必需) PLUGIN_NAME?: string; // 插件静态名称(可选,用于类型安全) init: (ctx: CPluginCtx<C>) => Promise<void>; // 初始化方法(必需) destroy: (ctx: CPluginCtx<C>) => Promise<void>; // 销毁方法(必需) reload?: (ctx: CPluginCtx<C>) => Promise<void>; // 重载方法(可选) export: (ctx: CPluginCtx<C>) => E; // 导出 API(必需) meta: { // 元数据(必需) engine: { version: string; }; };};
// 插件函数定义interface PluginFunction<C, E> { (ctx: CPluginCtx<C>): PluginObj<C, E>; ['PLUGIN_NAME']?: string;}
// CPlugin 可以是对象或函数export type CPlugin<C = Record<string, any>, E = any> = | PluginObj<C, E> // 直接是插件对象 | PluginFunction<C, E>; // 或者是返回插件对象的函数
// 插件上下文定义export type CPluginCtx<C = any> = { name?: string; // 插件名称 globalEmitter: Emitter<any>; // 全局事件发射器 emitter: Emitter<any>; // 插件私有事件发射器 config: C; // 插件配置对象 pluginManager: PluginManager; // 插件管理器 pluginReadyOk: () => void; // 通知插件已准备好 getWorkbench: () => Workbench; // 获取工作台实例 pageModel: CPage; // 页面模型 i18n: CustomI18n; // 国际化对象 assetsPackageListManager: AssetsPackageListManager; // 资源包管理器 engine: Engine; // 引擎实例};两种插件定义方式
方式 1:函数式定义(推荐)
export const MyPlugin: CPlugin = (ctx) => { // 可以访问 ctx 并创建闭包变量 let localState = {};
return { name: 'MyPlugin', async init(ctx) { // 初始化逻辑 ctx.pluginReadyOk(); }, async destroy(ctx) { // 清理逻辑 }, export: (ctx) => ({ // 导出的 API }), meta: { engine: { version: '1.0.0' }, }, };};方式 2:对象式定义
export const MyPlugin: CPlugin = { name: 'MyPlugin', async init(ctx) { ctx.pluginReadyOk(); }, async destroy(ctx) {}, export: (ctx) => ({}), meta: { engine: { version: '1.0.0' }, },};开发自定义插件
场景 1:开发一个自定义工具栏插件
假设我们要开发一个自定义工具栏插件,提供快速操作功能。
第一步:定义插件结构
import { CPlugin } from '@chamn/engine';import React from 'react';
const PLUGIN_NAME = 'CustomToolbar' as const;
export const CustomToolbarPlugin: CPlugin = (ctx) => { return { name: PLUGIN_NAME,
async init(ctx) { const workbench = ctx.getWorkbench();
// 添加顶部工具栏 workbench.addTopBarView({ name: PLUGIN_NAME, view: <CustomToolbarView ctx={ctx} />, });
// 通知插件已准备好 ctx.pluginReadyOk(); },
async destroy(ctx) { console.log('CustomToolbar 插件已卸载'); },
export: (ctx) => ({ // 暴露给外部的 API refresh() { console.log('刷新工具栏'); }, addButton(config: any) { console.log('添加自定义按钮', config); }, }),
meta: { engine: { version: '1.0.0', }, }, };};
CustomToolbarPlugin.PLUGIN_NAME = PLUGIN_NAME;第二步:实现工具栏 UI
import React, { useState } from 'react';import { CPluginCtx } from '@chamn/engine';
interface CustomToolbarViewProps { ctx: CPluginCtx;}
export const CustomToolbarView: React.FC<CustomToolbarViewProps> = ({ ctx }) => { const [selectedNode, setSelectedNode] = useState<any>(null);
// 监听节点选中事件 React.useEffect(() => { const handleSelect = (node: any) => { setSelectedNode(node); };
ctx.emitter.on('onSelectNode', handleSelect);
return () => { ctx.emitter.off('onSelectNode', handleSelect); }; }, [ctx]);
const handleCopy = () => { if (selectedNode) { const nodeData = ctx.pageModel.getNode(selectedNode.id); console.log('复制节点', nodeData); // 实现复制逻辑 } };
const handleDelete = () => { if (selectedNode) { ctx.pageModel.deleteNode(selectedNode.id); ctx.emitter.emit('onDeleteNode', selectedNode); } };
return ( <div style={{ display: 'flex', gap: '8px', padding: '8px' }}> <button onClick={handleCopy} disabled={!selectedNode}> 复制节点 </button> <button onClick={handleDelete} disabled={!selectedNode}> 删除节点 </button> <button onClick={() => ctx.pageModel.clear()}>清空画布</button> </div> );};第三步:使用插件
import { Engine, plugins } from '@chamn/engine';import { CustomToolbarPlugin } from './plugins/CustomToolbar';
const { DEFAULT_PLUGIN_LIST } = plugins;
function App() { const onReady = async (ctx) => { // 获取自定义工具栏插件 const toolbar = await ctx.pluginManager.get('CustomToolbar');
// 调用插件 API toolbar?.export.addButton({ label: '自定义按钮', onClick: () => console.log('点击了自定义按钮'), }); };
return ( <Engine plugins={[ ...DEFAULT_PLUGIN_LIST, CustomToolbarPlugin, // 添加自定义插件 ]} schema={pageSchema} material={materials} onReady={onReady} /> );}场景 2:开发带配置的插件
开发一个可配置的代码生成器插件。
import { CPlugin } from '@chamn/engine';
// 定义插件配置类型interface CodeGeneratorConfig { framework: 'react' | 'vue' | 'angular'; typescript: boolean; outputPath?: string;}
const PLUGIN_NAME = 'CodeGenerator' as const;
export const CodeGeneratorPlugin: CPlugin<CodeGeneratorConfig> = (ctx) => { // 从上下文获取配置 const config = ctx.config || { framework: 'react', typescript: true, };
return { name: PLUGIN_NAME,
async init(ctx) { const workbench = ctx.getWorkbench();
// 添加右侧面板 workbench.addRightPanel({ name: PLUGIN_NAME, title: '代码生成', view: <CodeGeneratorPanel config={config} ctx={ctx} />, });
ctx.pluginReadyOk(); },
async destroy(ctx) { console.log('CodeGenerator 插件已卸载'); },
export: (ctx) => ({ generate() { const schema = ctx.pageModel.export(); return generateCode(schema, config); }, setConfig(newConfig: Partial<CodeGeneratorConfig>) { Object.assign(config, newConfig); }, }),
meta: { engine: { version: '1.0.0', }, }, };};
function generateCode(schema: any, config: CodeGeneratorConfig) { // 根据配置生成代码 if (config.framework === 'react') { return generateReactCode(schema, config.typescript); } // ... 其他框架}
CodeGeneratorPlugin.PLUGIN_NAME = PLUGIN_NAME;使用带配置的插件
import { Engine, plugins } from '@chamn/engine';import { CodeGeneratorPlugin } from './plugins/CodeGenerator';
const { DEFAULT_PLUGIN_LIST } = plugins;
function App() { const beforePluginRun = ({ pluginManager }) => { // 在插件初始化前配置插件 pluginManager.customPlugin('CodeGenerator', (pluginInstance) => { pluginInstance.ctx.config = { framework: 'react', typescript: true, outputPath: './src/generated', }; return pluginInstance; }); };
return ( <Engine plugins={[...DEFAULT_PLUGIN_LIST, CodeGeneratorPlugin]} schema={pageSchema} material={materials} beforePluginRun={beforePluginRun} /> );}替换内置插件
Chameleon Engine 的所有内置插件都可以被替换。替换的核心思路是:不加载要替换的内置插件,而是加载自己的插件。
内置插件列表
import { plugins } from '@chamn/engine';
// 获取默认插件列表const { DEFAULT_PLUGIN_LIST } = plugins;
// 单独导入内置插件const { DesignerPlugin, // 设计器插件 ComponentLibPlugin, // 组件库插件 RightPanelPlugin, // 右侧属性面板插件 OutlineTreePlugin, // 大纲树插件 GlobalStatePanelPlugin, // 全局状态面板插件 HistoryPlugin, // 历史记录插件 HotkeysPlugin, // 快捷键插件} = plugins;
// 获取插件名称常量const { DEFAULT_PLUGIN_NAME_MAP } = plugins;// {// DesignerPlugin: 'Designer',// ComponentLibPlugin: 'ComponentLib',// RightPanelPlugin: 'RightPanel',// ...// }场景 1:替换属性面板插件
假设内置的 RightPanel 不满足需求,我们要完全替换它。
第一步:开发自定义属性面板
import { CPlugin } from '@chamn/engine';import React from 'react';
const PLUGIN_NAME = 'CustomRightPanel' as const;
export const CustomRightPanelPlugin: CPlugin = (ctx) => { return { name: PLUGIN_NAME,
async init(ctx) { const workbench = ctx.getWorkbench();
// 添加完全自定义的右侧面板 workbench.replaceRightPanel({ name: PLUGIN_NAME, view: <CustomPropertyEditor ctx={ctx} />, });
ctx.pluginReadyOk(); },
async destroy(ctx) { console.log('CustomRightPanel 插件已卸载'); },
export: (ctx) => ({ // 暴露与内置插件兼容的 API updateProperty(key: string, value: any) { const selectedNode = ctx.pageModel.getSelectedNode(); if (selectedNode) { ctx.pageModel.updateNode(selectedNode.id, { props: { ...selectedNode.props, [key]: value, }, }); } }, }),
meta: { engine: { version: '1.0.0', }, }, };};
CustomRightPanelPlugin.PLUGIN_NAME = PLUGIN_NAME;第二步:实现自定义属性编辑器
import React, { useState, useEffect } from 'react';import { CPluginCtx } from '@chamn/engine';
interface CustomPropertyEditorProps { ctx: CPluginCtx;}
export const CustomPropertyEditor: React.FC<CustomPropertyEditorProps> = ({ ctx }) => { const [selectedNode, setSelectedNode] = useState<any>(null); const [properties, setProperties] = useState<Record<string, any>>({});
useEffect(() => { const handleSelect = (node: any) => { setSelectedNode(node); if (node) { const nodeData = ctx.pageModel.getNode(node.id); setProperties(nodeData?.props || {}); } };
ctx.emitter.on('onSelectNode', handleSelect);
return () => { ctx.emitter.off('onSelectNode', handleSelect); }; }, [ctx]);
const handlePropertyChange = (key: string, value: any) => { if (selectedNode) { ctx.pageModel.updateNode(selectedNode.id, { props: { ...properties, [key]: value, }, }); setProperties((prev) => ({ ...prev, [key]: value })); } };
if (!selectedNode) { return <div style={{ padding: '16px' }}>请选择一个节点</div>; }
return ( <div style={{ padding: '16px' }}> <h3>自定义属性面板</h3> <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}> {Object.entries(properties).map(([key, value]) => ( <div key={key}> <label>{key}:</label> <input type="text" value={value} onChange={(e) => handlePropertyChange(key, e.target.value)} style={{ width: '100%', marginTop: '4px' }} /> </div> ))} </div> </div> );};第三步:替换内置插件
核心思路:从 DEFAULT_PLUGIN_LIST 中移除要替换的插件,然后添加自定义插件。
import { Engine, plugins } from '@chamn/engine';import { CustomRightPanelPlugin } from './plugins/CustomRightPanel';
const { DEFAULT_PLUGIN_LIST, RightPanelPlugin } = plugins;
function App() { // 1. 复制默认插件列表 const customPlugins = [...DEFAULT_PLUGIN_LIST];
// 2. 找到要替换的插件的索引 const rightPanelIndex = customPlugins.findIndex((plugin) => plugin === RightPanelPlugin);
// 3. 如果找到了,移除内置插件 if (rightPanelIndex !== -1) { customPlugins.splice(rightPanelIndex, 1); }
// 4. 添加自定义插件 customPlugins.push(CustomRightPanelPlugin);
return <Engine plugins={customPlugins} schema={pageSchema} material={materials} />;}简化写法(推荐)
import { Engine, plugins } from '@chamn/engine';import { CustomRightPanelPlugin } from './plugins/CustomRightPanel';
const { DEFAULT_PLUGIN_LIST, RightPanelPlugin } = plugins;
function App() { // 过滤掉要替换的插件,然后添加自定义插件 const customPlugins = [ ...DEFAULT_PLUGIN_LIST.filter((plugin) => plugin !== RightPanelPlugin), CustomRightPanelPlugin, ];
return <Engine plugins={customPlugins} schema={pageSchema} material={materials} />;}批量替换多个插件
import { Engine, plugins } from '@chamn/engine';import { CustomRightPanelPlugin } from './plugins/CustomRightPanel';import { CustomComponentLibPlugin } from './plugins/CustomComponentLib';
const { DEFAULT_PLUGIN_LIST, RightPanelPlugin, ComponentLibPlugin } = plugins;
function App() { // 要替换的插件列表 const pluginsToReplace = [RightPanelPlugin, ComponentLibPlugin];
// 过滤掉要替换的插件 const customPlugins = [ ...DEFAULT_PLUGIN_LIST.filter((plugin) => !pluginsToReplace.includes(plugin)), // 添加自定义插件 CustomRightPanelPlugin, CustomComponentLibPlugin, ];
return <Engine plugins={customPlugins} schema={pageSchema} material={materials} />;}场景 2:禁用某个插件
如果不需要某个内置插件,可以从 DEFAULT_PLUGIN_LIST 中过滤掉,然后将定制后的插件列表传递给引擎。
禁用单个插件
import { Engine, plugins } from '@chamn/engine';
const { DEFAULT_PLUGIN_LIST, GlobalStatePanelPlugin } = plugins;
function App() { // 过滤掉不需要的插件 const customPlugins = DEFAULT_PLUGIN_LIST.filter((plugin) => plugin !== GlobalStatePanelPlugin);
return <Engine plugins={customPlugins} schema={pageSchema} material={materials} />;}禁用多个插件
import { Engine, plugins } from '@chamn/engine';
const { DEFAULT_PLUGIN_LIST, GlobalStatePanelPlugin, OutlineTreePlugin, HotkeysPlugin } = plugins;
function App() { // 要禁用的插件列表 const pluginsToDisable = [GlobalStatePanelPlugin, OutlineTreePlugin, HotkeysPlugin];
// 过滤掉要禁用的插件 const customPlugins = DEFAULT_PLUGIN_LIST.filter((plugin) => !pluginsToDisable.includes(plugin));
return <Engine plugins={customPlugins} schema={pageSchema} material={materials} />;}使用插件名称禁用
import { Engine, plugins } from '@chamn/engine';
const { DEFAULT_PLUGIN_LIST, DEFAULT_PLUGIN_NAME_MAP } = plugins;
function App() { // 要禁用的插件名称列表 const pluginNamesToDisable = [ DEFAULT_PLUGIN_NAME_MAP.GlobalStatePanelPlugin, DEFAULT_PLUGIN_NAME_MAP.OutlineTreePlugin, ];
// 通过插件名称过滤 const customPlugins = DEFAULT_PLUGIN_LIST.filter((plugin) => !pluginNamesToDisable.includes(plugin.PLUGIN_NAME));
return <Engine plugins={customPlugins} schema={pageSchema} material={materials} />;}场景 3:使用 customPlugin 定制内置插件
如果只需要修改内置插件的部分行为,可以使用 customPlugin 而不是完全替换。重要:customPlugin 必须在插件初始化前调用,因此要在 beforePluginRun 回调中使用。
import { Engine, plugins } from '@chamn/engine';
const { DEFAULT_PLUGIN_LIST } = plugins;
function App() { const beforePluginRun = ({ pluginManager }) => { // 在插件初始化前定制它 pluginManager.customPlugin('History', (pluginInstance) => { // 修改插件配置 pluginInstance.ctx.config = { ...pluginInstance.ctx.config, maxHistoryLength: 100, // 自定义历史记录长度 enableRedo: true, };
// 修改插件导出的 API const originalExport = pluginInstance.export; pluginInstance.export = { ...originalExport, // 添加自定义方法 clearHistory() { console.log('清空历史记录'); originalExport.clear(); }, };
return pluginInstance; }); };
return ( <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} beforePluginRun={beforePluginRun} /> );}实现高度定制引擎
通过组合自定义插件和内置插件,可以实现完全定制的引擎。
场景 1:构建轻量级表单设计器
只保留必要的插件,移除不需要的功能。
import React from 'react';import { Engine, plugins } from '@chamn/engine';import { CustomFormToolbarPlugin } from './plugins/CustomFormToolbar';import { FormFieldLibraryPlugin } from './plugins/FormFieldLibrary';import { FormPropertyEditorPlugin } from './plugins/FormPropertyEditor';
const { DesignerPlugin, // 保留设计器 HistoryPlugin, // 保留历史记录 HotkeysPlugin, // 保留快捷键 OutlineTreePlugin, // 保留大纲树 // 移除其他不需要的插件} = plugins;
interface FormDesignerEngineProps { schema?: any; materials?: any; onSave?: (schema: any) => void;}
export const FormDesignerEngine: React.FC<FormDesignerEngineProps> = ({ schema, materials, onSave }) => { const customPlugins = [ // 内置核心插件 DesignerPlugin, HistoryPlugin, HotkeysPlugin, OutlineTreePlugin,
// 自定义表单插件 CustomFormToolbarPlugin, FormFieldLibraryPlugin, FormPropertyEditorPlugin, ];
const beforePluginRun = ({ pluginManager }) => { // 定制历史记录插件 pluginManager.customPlugin('History', (pluginInstance) => { pluginInstance.ctx.config = { maxHistoryLength: 50, }; return pluginInstance; }); };
const onReady = async (ctx) => { // 隐藏不需要的工作台组件 const workbench = ctx.getWorkbench(); workbench.hiddenWidget(['TopBar']);
// 获取自定义工具栏并配置保存功能 const toolbar = await ctx.pluginManager.get('CustomFormToolbar'); toolbar?.export.onSave(() => { const currentSchema = ctx.pageModel.export(); onSave?.(currentSchema); }); };
return ( <Engine plugins={customPlugins} schema={schema} material={materials} beforePluginRun={beforePluginRun} onReady={onReady} /> );};
// 使用表单设计器function App() { const handleSave = (schema: any) => { console.log('保存表单配置', schema); // 发送到服务器 };
return <FormDesignerEngine schema={initialFormSchema} materials={formFieldMaterials} onSave={handleSave} />;}场景 2:构建移动端页面编辑器
针对移动端场景定制引擎。
import React from 'react';import { Engine, plugins } from '@chamn/engine';import { MobilePreviewPlugin } from './plugins/MobilePreview';import { MobileComponentLibPlugin } from './plugins/MobileComponentLib';import { ResponsiveToolbarPlugin } from './plugins/ResponsiveToolbar';
const { DesignerPlugin, HistoryPlugin, HotkeysPlugin } = plugins;
interface MobilePageEditorProps { schema?: any; materials?: any; deviceType?: 'ios' | 'android';}
export const MobilePageEditor: React.FC<MobilePageEditorProps> = ({ schema, materials, deviceType = 'ios' }) => { const customPlugins = [ DesignerPlugin, HistoryPlugin, HotkeysPlugin, MobilePreviewPlugin, MobileComponentLibPlugin, ResponsiveToolbarPlugin, ];
const beforePluginRun = ({ pluginManager }) => { // 配置移动端预览插件 pluginManager.customPlugin('MobilePreview', (pluginInstance) => { pluginInstance.ctx.config = { deviceType, screenWidth: 375, showDeviceFrame: true, }; return pluginInstance; }); };
const onReady = async (ctx) => { const workbench = ctx.getWorkbench();
// 定制工作台布局 workbench.setLayout({ leftPanelWidth: 260, rightPanelWidth: 320, showTopBar: false, });
// 配置移动端特定的快捷键 const hotkeys = await ctx.pluginManager.get('Hotkeys'); hotkeys?.export.register({ 'cmd+p': () => { // 切换预览模式 const preview = await ctx.pluginManager.get('MobilePreview'); preview?.export.togglePreview(); }, 'cmd+d': () => { // 切换设备类型 const preview = await ctx.pluginManager.get('MobilePreview'); preview?.export.switchDevice(); }, }); };
return ( <Engine plugins={customPlugins} schema={schema} material={materials} beforePluginRun={beforePluginRun} onReady={onReady} /> );};场景 3:企业级定制引擎
集成权限控制、团队协作、版本管理等企业功能。
import React from 'react';import { Engine, plugins } from '@chamn/engine';import { AuthPlugin } from './plugins/Auth';import { CollaborationPlugin } from './plugins/Collaboration';import { VersionControlPlugin } from './plugins/VersionControl';import { AuditLogPlugin } from './plugins/AuditLog';import { TemplateMarketPlugin } from './plugins/TemplateMarket';
const { DEFAULT_PLUGIN_LIST } = plugins;
interface EnterpriseEngineProps { user: { id: string; name: string; role: 'admin' | 'editor' | 'viewer'; }; projectId: string; schema?: any; materials?: any;}
export const EnterpriseEngine: React.FC<EnterpriseEngineProps> = ({ user, projectId, schema, materials }) => { const enterprisePlugins = [ ...DEFAULT_PLUGIN_LIST, AuthPlugin, CollaborationPlugin, VersionControlPlugin, AuditLogPlugin, TemplateMarketPlugin, ];
const beforePluginRun = ({ pluginManager }) => { // 配置权限插件 pluginManager.customPlugin('Auth', (pluginInstance) => { pluginInstance.ctx.config = { user }; return pluginInstance; });
// 配置协作插件 pluginManager.customPlugin('Collaboration', (pluginInstance) => { pluginInstance.ctx.config = { projectId, wsUrl: 'wss://collaboration.example.com', }; return pluginInstance; });
// 配置版本控制插件 pluginManager.customPlugin('VersionControl', (pluginInstance) => { pluginInstance.ctx.config = { autoSave: true, saveInterval: 30000, }; return pluginInstance; }); };
const onReady = async (ctx) => { // 初始化权限控制 const auth = await ctx.pluginManager.get('Auth'); const permissions = auth?.export.getPermissions();
// 根据权限控制功能 if (permissions?.role === 'viewer') { // 只读模式 const designer = await ctx.pluginManager.get('Designer'); designer?.export.setReadonly(true);
const workbench = ctx.getWorkbench(); workbench.hiddenWidget(['LeftPanel', 'RightPanel']); }
// 启动协作 const collab = await ctx.pluginManager.get('Collaboration'); collab?.export.connect();
// 监听协作事件 ctx.emitter.on('collaboration:userJoin', (user: any) => { console.log(`${user.name} 加入了协作`); });
ctx.emitter.on('collaboration:change', (change: any) => { console.log('收到远程修改', change); });
// 启动自动保存 const versionControl = await ctx.pluginManager.get('VersionControl'); versionControl?.export.startAutoSave();
// 记录审计日志 const auditLog = await ctx.pluginManager.get('AuditLog'); auditLog?.export.log('PROJECT_OPENED', { projectId, user: user.name, }); };
return ( <Engine plugins={enterprisePlugins} schema={schema} material={materials} beforePluginRun={beforePluginRun} onReady={onReady} /> );};
// 使用企业版引擎function App() { const currentUser = { id: '123', name: 'John Doe', role: 'editor' as const, };
return ( <EnterpriseEngine user={currentUser} projectId="project-456" schema={projectSchema} materials={projectMaterials} /> );}插件开发最佳实践
1. 插件命名规范
// ✅ 推荐:使用描述性名称和常量const PLUGIN_NAME = 'CustomFormBuilder' as const;
export const CustomFormBuilderPlugin: CPlugin = (ctx) => { return { name: PLUGIN_NAME, // ... };};
CustomFormBuilderPlugin.PLUGIN_NAME = PLUGIN_NAME;
// ❌ 不推荐:使用字符串字面量export const MyPlugin: CPlugin = (ctx) => { return { name: 'my-plugin', // 难以追踪和重构 // ... };};2. 正确管理生命周期
export const MyPlugin: CPlugin = (ctx) => { let unsubscribeList: Array<() => void> = [];
return { name: 'MyPlugin',
async init(ctx) { // 订阅事件 const off1 = ctx.emitter.on('onSelectNode', handleSelect); const off2 = ctx.globalEmitter.on('onPageChange', handlePageChange);
unsubscribeList.push(off1, off2);
// ✅ 重要:通知插件已准备好 ctx.pluginReadyOk(); },
async destroy(ctx) { // ✅ 清理所有订阅 unsubscribeList.forEach((off) => off()); unsubscribeList = [];
// ✅ 清理其他资源 // 移除 DOM 节点、取消定时器等 },
export: () => ({}),
meta: { engine: { version: '1.0.0' }, }, };};3. 使用 TypeScript 类型安全
// 定义配置类型interface MyPluginConfig { theme: 'light' | 'dark'; locale: string; features: { autoSave: boolean; collaboration: boolean; };}
// 定义导出 API 类型interface MyPluginExport { setTheme(theme: 'light' | 'dark'): void; save(): Promise<void>; load(id: string): Promise<any>;}
// 使用类型export const MyPlugin: CPlugin<MyPluginConfig, MyPluginExport> = (ctx) => { const config = ctx.config || { theme: 'light', locale: 'zh-CN', features: { autoSave: true, collaboration: false, }, };
return { name: 'MyPlugin',
async init(ctx) { // ctx.config 现在有类型提示 console.log(config.theme); ctx.pluginReadyOk(); },
async destroy(ctx) {},
export: (ctx): MyPluginExport => ({ setTheme(theme) { config.theme = theme; }, async save() { // 实现保存逻辑 }, async load(id) { // 实现加载逻辑 return {}; }, }),
meta: { engine: { version: '1.0.0' }, }, };};4. 插件间通信
// PluginA:发送事件export const PluginA: CPlugin = (ctx) => { return { name: 'PluginA',
async init(ctx) { // 使用全局事件总线通信 ctx.globalEmitter.emit('pluginA:dataReady', { data: 'some data', });
ctx.pluginReadyOk(); },
async destroy(ctx) {},
export: (ctx) => ({ notifyChange(data: any) { ctx.globalEmitter.emit('pluginA:change', data); }, }),
meta: { engine: { version: '1.0.0' }, }, };};
// PluginB:接收事件export const PluginB: CPlugin = (ctx) => { return { name: 'PluginB',
async init(ctx) { // 等待 PluginA 准备好 await ctx.pluginManager.onPluginReadyOk('PluginA');
// 监听 PluginA 的事件 ctx.globalEmitter.on('pluginA:change', (data) => { console.log('收到 PluginA 的变化', data); });
// 获取 PluginA 实例并调用其 API const pluginA = await ctx.pluginManager.get('PluginA'); pluginA?.export.notifyChange({ from: 'PluginB' });
ctx.pluginReadyOk(); },
async destroy(ctx) {},
export: () => ({}),
meta: { engine: { version: '1.0.0' }, }, };};5. 错误处理
export const MyPlugin: CPlugin = (ctx) => { return { name: 'MyPlugin',
async init(ctx) { try { // 可能失败的初始化逻辑 await initializePlugin();
ctx.pluginReadyOk(); } catch (error) { console.error('MyPlugin 初始化失败', error);
// 发送错误事件 ctx.globalEmitter.emit('plugin:error', { plugin: 'MyPlugin', error, });
// 仍然标记为准备好,避免阻塞其他插件 ctx.pluginReadyOk(); } },
async destroy(ctx) { try { await cleanupPlugin(); } catch (error) { console.error('MyPlugin 清理失败', error); } },
export: (ctx) => ({ async doSomething() { try { // 业务逻辑 return { success: true }; } catch (error) { console.error('操作失败', error); return { success: false, error }; } }, }),
meta: { engine: { version: '1.0.0' }, }, };};总结
通过本指南,你已经学会了:
-
开发自定义插件
- 创建插件的基本结构
- 实现插件的生命周期
- 暴露插件 API
- 处理事件和状态
-
替换内置插件
- 完全替换内置插件
- 使用
customPlugin定制内置插件 - 保持 API 兼容性
-
实现高度定制引擎
- 构建轻量级编辑器
- 针对特定场景定制
- 企业级功能集成
-
最佳实践
- 命名规范
- 生命周期管理
- 类型安全
- 插件间通信
- 错误处理
现在你可以根据自己的需求,构建完全定制化的 Chameleon Engine 了!