Appearance
插件依赖与互调用
插件可以依赖其他插件(前置插件),并在插件之间互相调用方法。典型的场景:MarkdownLib 作为库插件提供 renderMarkdown 方法,其他插件依赖它并在页面里调用。
一、声明依赖
在 manifest 的 dependencies 字段声明:
json
{
"id": "top.qomicex.assistant",
"dependencies": [
{ "id": "top.qomicex.markdown", "version": ">=1.0.0" },
{ "id": "top.qomicex.themes", "version": ">=1.0 <2.0", "optional": true }
]
}| 字段 | 说明 |
|---|---|
id | 被依赖插件的 manifest id |
version | 版本范围(语法见 manifest 详解),缺省=任意 |
optional | true 可选前置;缺省 false = 必装前置 |
二、必装前置(optional: false)
- 安装时:缺失或版本不满足 → 拒绝安装,后端返回
PLUGIN_MISSING_DEPENDENCY(400),错误消息列出缺失项 - 启动时:前置插件未安装或未启用 → 该插件跳过激活并置为 disabled
- 用户需先安装前置插件,才能安装依赖它的插件
三、可选前置(optional: true)
- 安装 / 激活不强制检查
- 仅当前置插件存在且已启用时,
callPlugin才能调用其方法 - 前置缺失时调用
callPlugin报错,但插件本身正常启用 - 调用可选前置的方法时务必 try/catch:
js
try {
const result = await __PLUGIN_API__.callPlugin('top.qomicex.themes', 'applyTheme', 'dark')
} catch (e) {
// 可选前置缺失或未激活
}四、库插件惯例(被依赖方)
库插件(如 MarkdownLib)通常:
- 不声明
contributes.menuItems→ 不会在侧边栏创建任何入口 - 只声明
entry.frontend(哪怕是一个空白页)+ 激活时registerMethod permissions至少包含config:write(registerMethod需要)
json
{
"id": "top.qomicex.markdown",
"name": "MarkdownLib",
"version": "1.2.0",
"minLauncherVersion": "0.1.0",
"layers": ["l3"],
"permissions": ["config:write"],
"entry": { "frontend": "dist/index.html" }
}WARNING
库插件的 layers 用纯 ["l3"](内联)或 ["l2", "l3"](沙箱)均可。要点:
- 内联(
["l3"]):插件激活加载时脚本立即执行并注册方法,但 installed 状态不会自动激活,需用户手动启用一次后才随启动激活 - 含
l2(如["l2","l3"]):随启动自动激活,无需手动启用 见 layers 图层定义。
WARNING
库插件的 layers 不要用纯 ["l3"] —— 纯 l3 的 installed 插件不会自动激活,方法将无法注册,导致依赖方 callPlugin 失败。请使用 ["l2", "l3"](见 layers 图层定义)。
WARNING
库插件必须有 entry.frontend。只有声明了 frontend 的插件才会被激活(从而执行脚本、注册方法)。省略 frontend 的插件不会被激活,方法也就无法注册。
五、提供方:注册方法
被依赖插件在激活后的页面脚本中注册方法:
html
<!-- MarkdownLib dist/index.html -->
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
</head>
<body>
<div id="root">MarkdownLib 已加载</div>
<script>
__PLUGIN_API__.registerMethod('renderMarkdown', function (md) {
return marked.parse(md || '')
})
__PLUGIN_API__.registerMethod('stripHtml', function (html) {
const div = document.createElement('div')
div.innerHTML = html
return div.textContent || ''
})
</script>
</body>
</html>registerMethod(method, fn):fn返回值为值或 Promise(异步方法同样支持)- 一个插件可注册多个方法
- 插件停用时自动注销全部方法
- 权限:
config:write
六、调用方:调用方法
html
<!-- AI 助手 dist/index.html -->
<!DOCTYPE html>
<html>
<body>
<div id="out"></div>
<script>
async function render() {
try {
const html = await __PLUGIN_API__.callPlugin('top.qomicex.markdown', 'renderMarkdown', '**加粗**')
document.getElementById('out').innerHTML = html
} catch (e) {
document.getElementById('out').textContent = '渲染失败: ' + e.message
}
}
render()
</script>
</body>
</html>- 调用签名:
callPlugin(插件Id, 方法名, ...参数) - 权限:
network:fetch - 支持调用异步方法(提供方返回 Promise 时直接 await)
七、激活顺序
启动器启动时对被依赖插件先排序激活(拓扑排序 sortByDependencies),保证依赖方激活时前置插件已就绪并注册好方法。因此无需关心 manifest 里的声明顺序。
八、错误处理
| 场景 | 错误信息 |
|---|---|
| 目标插件未安装 / 未激活 / 方法未注册 | 插件 xxx 未提供方法 yyy(可能未安装或未激活) |
| 可选前置缺失 | 同上,但插件本身正常启用 |
| 方法内部抛错 | 插件 xxx 方法 yyy 执行失败: <具体错误> |
完整流程
- 用户安装 MarkdownLib → 成功(无依赖)
- 用户安装 AI 助手 → 后端检查依赖:markdown 已装且
1.2.0 >= 1.0.0→ 成功 - 启动器启动 → 先激活 MarkdownLib(注册
renderMarkdown)→ 再激活 AI 助手 - AI 助手
callPlugin('top.qomicex.markdown', 'renderMarkdown', md)→ 主窗口__pluginRegistry中转 → 返回渲染结果