Wujies 无界平台
多端前端后台管理

远程应用接入

后台前端最特殊的一点:它在构建期不可能知道某个租户装了哪些应用。应用前端因此不能被打进宿主 bundle,只能在运行期按后端清单 import()。

这一页讲清楚三件事:加载链路、硬约定、坏了怎么查。

加载链路

① 用户登录
   ↓
② 宿主启动,安装运行时:installAppMarketRuntime()
   → globalThis.__APP_MARKET_RUNTIME__ = { vue, router, pinia, elementPlus, request, modal, ... }
   ↓
③ 问后端:GET /app/runtime/remote-apps
   → [{ appCode: 'exam', entry: '/remote-apps/exam/index.js?v=1.2.0' }, ...]
   ↓
④ 动态 import(entry)
   → 远程模块导出 install(runtime) / default
   ↓
⑤ 远程应用返回 { manifest, views }
   → loader 注册视图键与 vue-router 路由
   ↓
⑥ 菜单 component = "remote:exam/settings" 命中视图键
   → 渲染远程页面

对应源码:

阶段文件
安装运行时src/plugins/appmarket/runtime.ts
拉入口清单src/plugins/appmarket/bootstrap.ts
加载 / 注册 / 卸载src/plugins/appmarket/loader.ts
常量约定src/plugins/appmarket/constants.ts
远程应用加载链路(示意图占位)

远程应用加载链路(示意图占位)

/images/docs/admin-remote-app.png

可画:宿主 → 后端入口清单 → ESM 产物 → 视图/路由注册 → 菜单渲染。

三个关键约定

1. 菜单只写视图键

remote:<appCode>/<viewKey>

例如 remote:exam/exam/settings:

  • appCode = exam
  • viewKey = exam/settings

remote: 前缀是显式声明,用来区分「宿主页面但路径写错」和「由远程应用提供」。没有这个前缀的 component 一律按宿主页面处理。

2. 入口 URL 必须带 ?v=<version>

/remote-apps/exam/index.js?v=1.2.0

不带版本号时,升级后浏览器会继续吃旧缓存,表现是「升级成功了,但页面还是老样子」。

3. 裸模块必须走 import map

远程产物里不会打进 Vue / Element Plus,import { ref } from 'vue' 由 index.html 的 import map 指到 /app-runtime/vue.js,垫片再转发到宿主正在用的那一份实例。

<script type="importmap">
{
  "imports": {
    "vue": "/app-runtime/vue.js",
    "vue-router": "/app-runtime/vue-router.js",
    "pinia": "/app-runtime/pinia.js",
    "element-plus": "/app-runtime/element-plus.js",
    "@dromara/request": "/app-runtime/dromara-request.js",
    "@dromara/components": "/app-runtime/dromara-components.js",
    "@dromara/modal": "/app-runtime/dromara-modal.js"
  }
}
</script>

这不是可选优化:如果远程应用用了另一份 Vue,会出现「双 Vue」——响应式、组件注册、inject 全部失效。

按需加载与等待

后端菜单可能先返回、应用产物后到。loader.ts 的处理是:

  1. 视图未注册时,先用 entryResolver(appCode) 向 bootstrap 要入口;
  2. import() 后注册视图,再唤醒 waitForRemoteView 的等待方;
  3. 等待超时默认 8000ms(REMOTE_VIEW_WAIT_TIMEOUT)。

所以「刷新后第一次点菜单慢半拍」是正常的;一直转圈才是问题。

排查表

现象根因处理
应用 xxx 未安装或入口地址未配置后端清单没有该应用,或应用未启用去「我的应用」确认已安装并启用
Failed to fetch dynamically imported module产物 404:没构建、目录名不对、?v= 指到旧文件检查 app-market-dist/remote-apps/<appCode>/index.js
远程应用未导出安装函数产物入口不是 install() / default检查应用的 src/index.ts
视图键冲突两个应用用了同一个 viewKey改成 <appCode>/<view> 命名空间
页面空白,控制台报 Component is missing template视图加载器返回了 defineAsyncComponent 包装器按 loader 约定返回 { default: Component }
双 Vue / inject 失效import map 或垫片被改重跑 npm run gen:runtime && npm run check:runtime

更底层的运行时机制见 前端远程应用(宿主侧)。

目录