Wujies 无界平台
多端前端

前端运行时 API

远程应用(插件前端)不 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.requestawait runtime.request({ url: '/captchaConfig', method: 'get' });DemoPluginDev.vue 还演示了匿名调用 { headers: { isToken: false } }views/DemoHome.vue:25、views/DemoPluginDev.vue:74
runtime.vueruntime.vue.versionviews/DemoHome.vue:54、views/DemoPluginDev.vue:131
runtime.elementPlusruntime.elementPlus.versionviews/DemoHome.vue:55、views/DemoPluginDev.vue:132
runtime.platformVersion直接展示,用于兼容降级判断views/DemoHome.vue:56、views/DemoPluginDev.vue:133
runtime.routerInstancetypeof runtime.routerInstance?.addRoute(证明拿到的是真实 router 实例)views/DemoHome.vue:58

hello-app 自己的 src/runtime.ts 只是一层持有者:install 时 setRemoteRuntime(runtime), 视图里 getRemoteRuntime() 读;没被注入过就抛错 ([hello] 宿主运行时尚未注入,请确认应用通过 install(runtime) 装载), 这样"忘了走 install"会立刻暴露,而不是变成一个 undefined 引发的怪错误。

完整字段表

以下来自宿主侧的类型定义(AppMarketRuntime),不是推测:

字段类型说明
vueVue 模块命名空间宿主正在用的那一份实例,不是重新 import 的副本
routervue-router 模块命名空间同上(模块本身;路由操作看 routerInstance)
piniapinia 模块命名空间同上
elementPlusElement 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' 两种写法都能解析到宿主这一份
routerInstanceRouter宿主 vue-router 实例,用于运行期注册 / 卸载远程路由
componentsRecord<string, Component>宿主共享 UI 组件(KCard / KTable / KSearchBar / ImagePreview 等)。要用宿主已有业务组件必须从这里取,不要自己打一份进 bundle
modalRecord<string, (...args: any[]) => any>宿主 modal 插件(msgSuccess / msgError / confirm / loading 等)
appApp(可选)宿主 Vue 应用实例。指令必须靠它:v-loading 会被编译成运行期的 resolveDirective("loading"),只有全局注册过才解析得到
platformVersionstring平台版本(由宿主 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),调试时可以直接在控制台取。

相关阅读

目录