Skip to content

子应用(微前端)加载优化方案

状态:已实现(代码完成,待部署) 日期:2026-07-03 适用对象:所有 qiankun 子应用(当前:vem-admin、admin-page-design;后续新子应用同规范)

现状与问题

以 vem-admin 为例(hiapi-cloud-vem/vem-admin):

产物体积说明
hiapi-cloud-vem-admin.umd.js2.3 MBUMD 单文件,无代码分割
vem-admin.css400 KB基本是 element-plus 全量 CSS

体积构成(按依赖估算):

  • element-plus 全量(JS ≈ 800KB + CSS 400KB)—— 主应用已加载一份,纯重复
  • echarts 全量(≈ 1MB)—— 只有报表等少数页面用到
  • vue / vue-router / vue-i18n(≈ 150KB)—— 主应用也有一份
  • moveable、业务代码等

传输层问题:

  • OSS 直链不压缩,2.3MB 裸传
  • entry URL 固定文件名(hiapi-cloud-vem-admin.umd.js)覆盖式发布,无法强缓存,每次进入都可能重新下载

方案一:外置共享依赖(收益最大)

qiankun 正统做法:主应用把公共库挂到 window,子应用打包时 external 掉。qiankun JS 沙箱内子应用可读取主应用 window 属性。

主应用改动(hiapi-cloud-admin-ts/src/main.ts):

ts
import * as Vue from 'vue'
import ElementPlus from 'element-plus'
// 挂到 window 供子应用使用
;(window as any).Vue = Vue
;(window as any).ElementPlus = ElementPlus

子应用改动(vite.config.ts):

ts
build: {
  rollupOptions: {
    external: ['vue', 'element-plus'],
    output: {
      globals: { 'vue': 'Vue', 'element-plus': 'ElementPlus' }
    }
  }
}

同时删除子应用中的两行 CSS import(main.ts):

ts
// 删除:主应用已全局加载,qiankun 未开样式隔离,子应用直接可用
import 'element-plus/dist/index.css'
import 'element-plus/theme-chalk/dark/css-vars.css'

注意事项:

  • 版本对齐:vue、element-plus 以主应用版本为准(当前主应用 element-plus 2.13.x、vue 3.5.x),子应用 package.json 保持同一 major/minor
  • 子应用独立开发模式(非 qiankun 环境 pnpm dev)时 window 上没有这些全局变量 —— external 只作用于 build,dev 走正常模块解析,不受影响
  • @element-plus/icons-vue 体积小,可暂不外置
  • vue-router / vue-i18n 子应用有独立实例需求,不建议外置

预期收益:JS 2.3MB → 约 600KB,CSS 400KB → 接近 0。

方案二:echarts 按需引入

核查结论:vem-admin 已经是 echarts/core 按需注册(ChartCard.vue 只注册了 LineChart/Grid/Tooltip/CanvasRenderer),无需改动。体积大头确认为 element-plus 全量 + vue 全家桶,由方案一解决。后续新子应用沿用按需写法即可。

方案三:传输压缩 + 版本化强缓存

压缩:OSS 直链默认不做 gzip/brotli。两条路:

  • 接 CDN(阿里云 CDN 对 js/css 开智能压缩/Brotli),传输体积约 -70%
  • 或 UploadPlugin 发布时预压缩上传 .js.gz 并设置 Content-Encoding: gzip 元数据(无 CDN 也生效)

版本化 URL + 强缓存(与《子应用菜单翻译与图标方案》的 admin.json 机制合并实现):

  • nuwa 发版时把 js/css 上传到版本目录 hiapi-cloud/{project}/{version}/(对象设 Cache-Control: public, max-age=31536000, immutable),并保留根目录 latest 副本向后兼容(Cache-Control: no-cache)
  • 同时生成 admin.json(name/activeRule/scripts/styles/i18n/icons 汇总清单)
  • cloud-public 平台安装/升级时把 admin.json 存入 CloudApplication.adminEntry
  • 主应用启动时 GET /cloud-api/public/micro-apps(公开接口)获取动态清单,与静态兜底合并后 registerMicroApps

效果:发版即生效(URL 带版本号),未发版时老用户二次进入零下载;同时主应用彻底摆脱硬编码 entry。


实施顺序建议

  1. 方案一(主应用挂 window + 子应用 external + 删 CSS)
  2. 方案三(nuwa 版本目录 + Cache-Control + admin.json + 主应用动态 entry),与菜单翻译/图标方案同批改造 nuwa 与主应用

预计首屏传输量从 ~2.7MB 降到数百 KB(压缩后更低),且二次进入零下载。

实测结果(vem-admin,2026-07-03)

干跑构建(disable:true 不上传)逐步对比:

阶段umd.js(raw)umd.js(gzip)css(raw)
优化前2,329 KB~700 KB404 KB
external vue+element-plus + 删重复CSS1,399 KB416 KB51 KB
再 external icons-vue+vue-router+vue-i18n1,122 KB342 KB51 KB

已挂到主应用 window 的公共库(版本必须与子应用一致):Vue / VueRouter / VueI18n / ElementPlus / ElementPlusIconsVue。子应用 vite.config external 这 5 个 + globals。CSS 由主应用全局提供,子应用删除 element-plus CSS import(dev 用 index.html CDN 兜底)。

剩余体积构成与 UMD 硬上限

实测把 echarts+moveable 也 external 掉后 umd 仅 369 KB —— 即 echarts+moveable ≈ 753 KB,是当前包体的绝对大头。

  • echarts 已是 echarts/core 按需(只 LineChart/Grid/Tooltip/CanvasRenderer),moveable 仅设备模板编辑页用到。
  • UMD 单文件 lib 模式禁用代码分割,动态 import 会被内联,所以 echarts/moveable 无法做路由级懒加载 —— 这是 1.1MB 降不下去的根因,不是没按需。
  • 把它们也挂主应用 window 属于把体积转嫁给主应用首屏(每个页面都加载),不可取。

要继续降到 ~400KB:改 qiankun html-entry(需决策)

放弃单文件 UMD,子应用改标准多文件构建 + vite-plugin-qiankun 注入生命周期,恢复代码分割:echarts 只在打开仪表盘/报表页时加载,moveable 只在模板编辑页加载,首屏 chunk 可降到 ~400KB。

代价:发布产物从「单 js+css」变成「整个 dist 目录(index.html + assets/*)」,需连带改:

  • UploadPlugin:上传整个 dist 目录而非固定文件列表
  • nuwa:admin.json 的 scripts/styles 改为指向 html-entry 或 chunk 清单
  • 主应用:registerMicroApps 的 entry 由 scripts 数组改为 html 地址

这是治本项、改动面较大,建议作为独立一期,待现有 UMD 优化上线验证后再做。

附:长期方案(本期不做)

放弃单文件 UMD lib 模式,改 qiankun html entry(配合 vite-plugin-qiankun):子应用恢复路由级代码分割,echarts 只在打开报表页时加载。代价是发布产物从单 js 变为整个 dist 目录,nuwa 发布流程需配套改造。待上面三项落地后再评估。