1. 前端插件
插件化手册
  • FH2 插件开发手册
    • 产品简介
    • 典型场景
    • 发版说明
    • 快速开始
      • 快速开始总览
      • 第一个插件
      • 后端插件
      • 全栈闭环
    • AI辅助开发
      • AI 开发 Skill
      • 安装与复验
      • 第一个工程
      • 真实需求示例:现场问题上报
      • 改造已有工程
      • 调试与交付
    • 开发指南
      • 开发指南
      • 基础概念
        • 基础概念总览
        • 什么是插件开发
        • 平台架构
        • 前端宿主、扩展点与能力
        • 后端运行时、入口与权限
        • 开发流程
        • 安全与隔离
        • 支持范围与问题反馈
      • 开发指南
        • 开发指南总览
        • 前端插件
          • 前端插件深度指南
          • 前端工程结构
          • 插件清单(fh2-plugin.json)
          • 入口与生命周期
          • 界面与状态
          • 多宿主插件
          • 跨宿主通信服务
          • HTTP、WebSocket 与事件
          • 存储、Blob 与定时器
          • 工具链与本地调试页面
        • 后端插件
          • 后端开发深度指南
          • 后端插件的结构
          • 后端源清单 fh2-plugin.json
          • HTTP 接口处理函数
          • 消息订阅与事件处理函数
          • ctx 与能力注入
          • 数据存储与缓存
          • 流程编排
          • 触发方式
          • 连接外部系统
          • 流程编排练习(DAG)
        • 前后端协作
          • 前后端协作总览
          • 前后端共享契约
          • 端到端幂等
          • 事件、通知与状态
          • 统一错误模型
          • 链路标识与诊断日志
        • 完整练习
          • 环境准备
          • 第一个前端插件
          • 第一个后端插件
          • 第一个全栈插件
      • 能力总览
        • 能力地图
        • 能力可用性矩阵
        • 前端能力
          • 平台通用能力
            • 平台通用能力总览
            • 基础运行时:HTTP、事件、WebSocket 与存储
            • 导航、位置与通知
            • 弹窗、音频与剪贴板
            • 地图能力
            • 项目设备能力
            • WPMZ 与航线预览
            • 受控对象上传
          • 项目宿主
            • 项目宿主总览
            • 项目上下文
            • 项目侧边栏入口
            • 项目插件路由
            • 项目工作区
          • 消息宿主
            • 消息宿主总览
            • 消息告警弹层
            • 消息导航与通知
          • 计划库宿主
            • 计划库宿主总览
            • 计划库表单上下文
            • 计划库表单扩展
            • 校验、提交与 `biz_ext`
          • 驾驶舱
            • 驾驶舱插件能力总览
            • 驾驶舱扩展点
            • 上下文与遥测
            • 相机、检测与 AI
            • 场景几何与直播指针
            • 飞控、云台与任务
            • 驾驶舱平台事件
        • 后端能力
          • 后端能力地图
          • 后端能力总览
          • 配置、受管出网与集成连接
          • 事件与流程
          • 飞行任务与违停巡逻
          • 数据存储
          • 媒体与前端通知
          • 资源租约
          • 作业、目标轨迹与视频处理
      • 调试与测试
        • 调试与测试总览
        • 快速入门排错
        • 本地开发
        • Web IDE:打开工程与保存改动
        • 工作流编辑器:节点、接线与交付
        • 知识与 MCP:先判断需要哪一层事实
        • 前端本地调试
        • 后端运行时与流程调试
        • 模拟数据与测试数据
        • 集成测试与 E2E
        • 日志与链路追踪
        • 常见问题排查
      • 完整示例
        • 示例与案例总览
        • 项目侧边栏与工作区
        • 消息告警卡片
        • 计划表单任务清单
        • 驾驶舱动作与左侧面板
        • 最小后端插件
        • 最小全栈插件
        • 多宿主插件实战
        • 事故快处
        • 交通抓拍
        • 违停巡逻完整案例
      • 构建发布与安全
        • 构建、发布与安全
        • 质量门禁
        • 行业包构建与打包
        • 能力版本发布
        • 安装、升级与回滚
        • 安全基线
      • API参考
        • API 参考总览
        • 前端
          • 前端 API 参考
          • 前端插件清单结构
          • 前端 SDK API
          • 前端扩展点目录
          • 前端能力目录
          • 前端平台事件目录
          • 跨宿主通信服务 API
          • 工具链命令行参考(`fh2-plugin`)
        • 后端
          • 后端 API 参考
          • 后端插件源清单
          • 后端 HTTP 接口参考
          • 后端事件消费参考
          • 后端脚本上下文(`ctx`)
          • 后端数据存储
          • 流程编排语言
          • 流程触发器参考
          • 后端能力目录
          • 后端 HTTP API
        • 共享契约
          • 共享契约参考
          • 类型与契约目录
          • 错误模型
          • 版本兼容性
      • 附录
        • 错误码
        • 术语表
        • 常见问题
  1. 前端插件

入口与生命周期

这一页讲入口代码 plugin.ts 的写法:宿主在什么时候调用它、activate 函数里该按什么顺序做事、插件被停止时怎么收尾。看完你就能解释"插件为什么没出现"和"插件卸载后为什么还在跑"这两类问题。

前置条件#

清单和生成类型已同步(改过清单就先 pnpm run generate)。
每个实际使用的宿主页面都有一个 src/hosts/<host>/activate.ts。
fh2-plugin-sdk 是插件开发 SDK;下文模块名用于说明,实际导入路径保留生成工程配置。模块名不表示已发布的 npm 包名。

唯一入口,按宿主分流#

一个插件只有一个入口文件。宿主加载插件后会调用它的 activate(ctx) 函数;同一个插件可能被多个页面激活,用 ctx.host.id 判断当前在哪个页面:
// src/plugin.ts
import { definePlugin } from 'fh2-plugin-sdk'
import type { PluginContext } from 'fh2-plugin-sdk'
import type { AppCockpitContext, AppMessageContext, AppProjectContext } from './generated/plugin-types'

export default definePlugin({
  async activate (ctx: PluginContext) {
    switch (ctx.host.id) {
      case 'cockpit':
        return (await import('./hosts/cockpit/activate')).activate(ctx as AppCockpitContext)
      case 'project':
        return (await import('./hosts/project/activate')).activate(ctx as AppProjectContext)
      case 'message':
        return (await import('./hosts/message/activate')).activate(ctx as AppMessageContext)
      default:
        return undefined
    }
  },
})
三条规则:
入口的 default export 必须是 definePlugin(...) 的结果。
只给清单里实际声明了扩展点的宿主写分支。比如插件没有计划页扩展,就不要写 plan 分支。
动态 import() 只是把各页面代码拆成小包,不会产生第二个入口或第二套生命周期。不要把 Vue、DOM 操作或界面挂载依赖引进 plugin.ts。

activate 里的推荐顺序#

每个 src/hosts/<host>/activate.ts 按这个顺序写,最不容易出错:
// src/hosts/project/activate.ts
import type { AppProjectContext } from '../../generated/plugin-types'
import { projectView } from '../../views/project-panel/contract'

export async function activate (ctx: AppProjectContext) {
  // 1. 取能力:可选能力可能拿不到(undefined),先处理缺席
  const project = await ctx.capabilities.get('project:context')
  if (!project) return

  // 2. 建立入口侧状态
  const snapshot = await project.getState()
  ctx.logger.info('当前项目', { projectId: snapshot.projectId })

  // 3. 注册界面处理函数(先于扩展提交,按钮打开界面时才不会落空)
  await projectView.handle(ctx, { /* state 与 methods,见界面与状态一页 */ })

  // 4. 提交扩展(按钮、面板……)
  await ctx.extensions.contribute('project-entry', {
    icon: 'plugin',
    label: '项目面板',
    disabled: snapshot.projectId === null,
    showBadge: false,
    render: () => projectView.get(ctx),
  })

  // 5. 最后启动订阅、定时器等持续型任务
}
不同宿主之间没有固定的激活顺序,不要假设"提供数据的宿主一定先激活"。

停止与收尾#

插件被停止时(用户切走页面、插件被禁用或卸载、配置变更等),平台会先冻结运行时,再让你清理。冻结之后 ctx.apiCall 和 ctx.storage 会直接报 HOST_RUNTIME_DISPOSED,登记过的订阅也已经在回收,所以 dispose 里只做收尾,不要在里面保存数据。
需要落盘的草稿,在状态变化时写,不要等到退出:
// 跟着输入写,退出时什么都不用补
async function saveDraft (text: string): Promise<void> {
  await ctx.storage.local.set('draft', { text })
}

return {
  dispose () {
    controller.stop()   // 平台管不到的第三方资源
  },
}
平台会自动回收它登记过的资源:扩展、界面、定时器(ctx.timers)、平台事件和 WebSocket 订阅、跨宿主通信服务等。只有平台管不到的东西才需要你在这里清理,比如第三方 SDK 的控制器、硬件句柄。
停止原因 reason 的取值:
reason含义
host-unmount宿主页面卸载
plugin-disabled插件被禁用
plugin-uninstalled插件被卸载
config-changed用户改了配置(之后会用新配置重新激活)
activation-failed激活过程出错
runtime-terminated运行时被强制终止
两个配合要点:
长异步操作每步检查 ctx.lifecycle.signal.aborted,已中止就尽快返回。
运行时停止后不要再更新界面状态——订阅已被回收,更新是无效写入。

日常命令与预期#

预期:同一个插件在每个宿主里各有一份独立运行时(各自的 ctx、ctx.host.instanceId 和状态),一个页面卸载不影响另一个页面。在本地调试页面里逐个宿主切换验证。

常见问题#

现象原因与处理
激活没报错,但页面上什么都没出现检查 ctx.host.id 分支是否覆盖了当前页面;contribute 的扩展 ID 是否与清单一致
能力调用不生效ctx.capabilities.get() 可能返回 undefined(当前宿主没提供),必须有缺席分支
插件停了但第三方任务还在跑平台只回收登记过的资源;第三方控制器要放进返回的 dispose 里停
改配置后插件"重启"了正常行为:配置变更会停止旧运行时并用新快照重新激活
想在一个宿主里访问另一个宿主的变量做不到,运行时互相独立;需要协作用跨宿主通信服务(见多宿主插件)
修改于 2026-10-25 13:35:59
上一页
插件清单(fh2-plugin.json)
下一页
界面与状态
Built with