前端运行时 API
本文是 1.x 的归档文档(冻结于 1.x)。该分支只接收勘误修正, 内容基本不再变动;文中的源码路径、配置项与版本号均以 1.x 为准,不建议用于新项目。
新项目请查看 5.x 最新文档。已在 1.x 上运行的项目,本文仍是可靠依据。
远程应用(插件前端)不 import 宿主任何源码。它拿到的全部东西,就是宿主在装载时
通过 install(runtime) 递进来的那一个对象。
export default function install(runtime: AppMarketRuntime) {
setRemoteRuntime(runtime);
return {
manifest: { code: 'hello', name: '示例远程应用', version: '0.1.0', routes: [ /* ... */ ] },
views: { 'demo/home': () => ({ default: DemoHome }) }
};
}| 项 | 值 |
|---|---|
| 入口契约 | 默认导出(也兼容具名 install)一个安装函数,返回 { manifest, views } |
| 类型定义 | plus-ui/src/plugins/appmarket/types.ts 的 AppMarketRuntime |
| 实例构造 | plus-ui/src/plugins/appmarket/runtime.ts 的 installAppMarketRuntime() |
| 范例用法 | wujies/wujies-plugins/frontend/hello-app/src/(runtime.ts / views/DemoHome.vue / views/DemoPluginDev.vue) |
范例应用实际用到的字段
hello-app 不是"文档里的例子",它的每个字段都能在页面上看到值:
| 字段 | 怎么用的 | 来源 |
|---|---|---|
runtime.request | await runtime.request({ url: '/captchaConfig', method: 'get' });DemoPluginDev.vue 还演示了匿名调用 { headers: { isToken: false } } | views/DemoHome.vue:25、views/DemoPluginDev.vue:74 |
runtime.vue | runtime.vue.version | views/DemoHome.vue:54、views/DemoPluginDev.vue:131 |
runtime.elementPlus | runtime.elementPlus.version | views/DemoHome.vue:55、views/DemoPluginDev.vue:132 |
runtime.platformVersion | 直接展示,用于兼容降级判断 | views/DemoHome.vue:56、views/DemoPluginDev.vue:133 |
runtime.routerInstance | typeof runtime.routerInstance?.addRoute(证明拿到的是真实 router 实例) | views/DemoHome.vue:58 |
hello-app 自己的 src/runtime.ts 只是一层持有者:install 时 setRemoteRuntime(runtime),
视图里 getRemoteRuntime() 读;没被注入过就抛错
([hello] 宿主运行时尚未注入,请确认应用通过 install(runtime) 装载),
这样"忘了走 install"会立刻暴露,而不是变成一个 undefined 引发的怪错误。
完整字段表
以下来自宿主侧的类型定义(AppMarketRuntime),不是推测:
| 字段 | 类型 | 说明 |
|---|---|---|
vue | Vue 模块命名空间 | 宿主正在用的那一份实例,不是重新 import 的副本 |
router | vue-router 模块命名空间 | 同上(模块本身;路由操作看 routerInstance) |
pinia | pinia 模块命名空间 | 同上 |
elementPlus | Element Plus 模块命名空间 | 同上;el-* 组件由 unplugin-vue-components 构建期转成显式导入,远程应用要自己配(见 gallery-app/vite.config.ts) |
request | <T = any>(config: Record<string, any>) => Promise<T> | 宿主 axios 实例:已带 baseURL / token / 租户头 / 加解密 / 统一错误处理。它同时挂着具名导出 download / globalHeaders / isRelogin,所以产物里 import request from '@dromara/request' 与 import { globalHeaders } from '@dromara/request' 两种写法都能解析到宿主这一份 |
routerInstance | Router | 宿主 vue-router 实例,用于运行期注册 / 卸载远程路由 |
components | Record<string, Component> | 宿主共享 UI 组件(KCard / KTable / KSearchBar / ImagePreview 等)。要用宿主已有业务组件必须从这里取,不要自己打一份进 bundle |
modal | Record<string, (...args: any[]) => any> | 宿主 modal 插件(msgSuccess / msgError / confirm / loading 等) |
app | App(可选) | 宿主 Vue 应用实例。指令必须靠它:v-loading 会被编译成运行期的 resolveDirective("loading"),只有全局注册过才解析得到 |
platformVersion | string | 平台版本(由宿主 constants.ts 的常量给出,当前为 5.6.2),远程应用可据此做兼容降级 |
components 里组件的 props / 事件、request 的调用形态,一旦有远程应用在用就不能改签名 ——
改了会破坏已经发布出去的插件包。新增具名导出时,还要同时更新宿主的
scripts/gen-runtime-shims.mjs 里对应库的 staticNames,再跑 npm run check:runtime 对账,
否则浏览器会在链接期整块报 does not provide an export named ...,页面直接打不开。
views 与 manifest 的约定
| 契约 | 规则 |
|---|---|
views 的加载器 | 必须返回 { default: Component }(与 import() / defineAsyncComponent 的语义一致),也可以是返回它的 Promise |
manifest.code | 应用编码,与 app.json 的 code、产物目录名一致 |
manifest.routes[].view | 视图键,必须存在于 views 里;菜单里 component 写 remote:<code>/<视图键> |
manifest.routes[].name | 可省,缺省时自动生成 <appCode>:<view> |
manifest.routes[].parentName | 不传则注册为顶级路由 |
manifest.routes[].meta | 附加路由元信息(标题、图标、是否缓存等) |
manifest.styles | 自带样式表 URL。ESM 产物里的 CSS 不会随 import() 自动注入,必须由宿主显式插 <link>,否则远程页面无样式 |
宿主拿到返回值后会:索引视图键 → 注册路由 → (可选)注入样式。
运行时的实例同时还挂在 globalThis 的 __APP_MARKET_RUNTIME__ 键上
(APP_MARKET_RUNTIME_KEY),调试时可以直接在控制台取。
相关阅读
- 开放接口清单 ——
runtime.request能打的/open/**端点 - 前端远程应用 —— 前端工程的构建配置、组件白名单与运行时垫片
- 前端远程应用(架构) —— 宿主侧怎么加载与挂载这份 ESM
- 跟着 hello 读源码 ——
install(runtime)与三个视图的真实文件