Skip to content

插件依赖与互调用

插件可以依赖其他插件(前置插件),并在插件之间互相调用方法。典型的场景: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 详解),缺省=任意
optionaltrue 可选前置;缺省 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:writeregisterMethod 需要)
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 执行失败: <具体错误>

完整流程

  1. 用户安装 MarkdownLib → 成功(无依赖)
  2. 用户安装 AI 助手 → 后端检查依赖:markdown 已装且 1.2.0 >= 1.0.0 → 成功
  3. 启动器启动 → 先激活 MarkdownLib(注册 renderMarkdown)→ 再激活 AI 助手
  4. AI 助手 callPlugin('top.qomicex.markdown', 'renderMarkdown', md) → 主窗口 __pluginRegistry 中转 → 返回渲染结果