插件机制的本质:扩展点 + 回调
目录
1 简要回答
所有插件机制本质相同:宿主留扩展点(接口),插件注册回调函数,宿主在关键时刻调用回调。配置文件覆盖、Obsidian 插件、游戏 Mod 全是这个套路。
2 详细回答
2.1 1. 配置覆盖原理
以 opencode 模型名显示为例:opencode 启动时读取配置并与模型数据合并(数据在 ~/.cache/opencode/models.json),选择器渲染时取合并后的 model.name。所以在 opencode.jsonc 里写:
| |
就能覆盖显示名。改配置 → 改数据 → UI 自动变,没碰任何程序代码。
2.2 2. 插件机制的通用四步
- 扩展点:宿主在关键流程留接口(消息发送前、工具执行后、模型显示时…)
- 注册:插件把函数注册到挂钩点(宿主保存回调列表,不写死行为)
- 调用:宿主在合适时机遍历回调列表依次调用
- 公开 API:插件只能通过宿主开放的 API 交互,拿不到内部实现
2.3 3. C++ 类比
- 接口 + 虚函数:
class IPlugin { virtual void onLoad() = 0; },插件实现接口 - Windows:DLL 动态加载(
LoadLibrary) - 设计模式:观察者模式 / 回调函数指针
2.4 4. 插件的写法
| |
加载:plugin: ["npm包名", "./本地.ts"],或放 .opencode/plugin/ 自动发现。宿主启动时调用导出函数拿到 hooks,之后事件发生时调用对应 hook。
2.5 5. 对照:Obsidian 插件
manifest.json 声明 → Obsidian 扫描插件目录 → 实例化 class extends Plugin 并调用 onLoad() → 在 onLoad 里注册 ribbon 图标、状态栏、命令。和 export default function 是同一件事的两种写法:宿主调用插件注册的回调。
3 知识扩展
- 事件总线:宿主发布事件,插件订阅(
eventhook 就是这个) - 生命周期:发现插件 → 加载模块 → 拿 hooks → 绑定事件 → 卸载时清理
- 宿主不直接引用插件类型,通过约定的接口/函数签名解耦——插件和插件之间互不感知