工程实践

远程应用前端:怎么避免「双 Vue」

Wujies 团队

宿主在构建期不知道租户装了哪些应用,所以应用前端只能运行期 import()。这篇讲 import map、运行时垫片和「双 Vue」是怎么被解决的。

后台前端在构建时不可能知道某个租户装了哪些应用。这意味着应用的前端产物不能被打进宿主 bundle,只能在运行期按后端清单 import()。

入口从哪来

宿主启动后调用 GET /app/runtime/remote-apps,拿到已启用应用的入口清单;菜单里的 component 写成 remote:<appCode>/<viewKey>,只是一个视图键,不是文件路径。

菜单:remote:exam/exam/settings
  appCode = exam
  viewKey = exam/settings
入口:/remote-apps/exam/index.js?v=1.2.0

双 Vue 是怎么来的

远程产物里 import { ref } from "vue"。如果 Rollup 把 vue 打进产物,页面里就会有两个 Vue 实例:响应式失效、组件注册不上、inject 拿不到宿主提供的东西。

解决办法是让裸模块说明符在浏览器里解析到宿主同一份实例:index.html 的 import map 把 vue 指到 /app-runtime/vue.js,垫片再转发到 globalThis.__APP_MARKET_RUNTIME__。

<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"
  }
}
</script>

三个容易踩的坑

  • 入口 URL 不带 ?v=:升级后浏览器吃旧缓存,表现为「升级成功了但页面没变」。
  • AutoImport 清单少一项:产物里出现裸全局,构建不报错,一装载就 ReferenceError。
  • 自己写「薄垫片」绕 SDK:把宿主源码拉进产物,随后就是缺依赖、双实例、版本漂移。

怎么验收

改完远程应用,至少跑 check:runtime、check:globals、check:remote 三条校验,再在浏览器里装一个应用点开菜单。不要只验证「构建成功」——构建成功和运行成功是两件事。