Appearance
子应用(微前端)加载优化方案
状态:已实现(代码完成,待部署) 日期:2026-07-03 适用对象:所有 qiankun 子应用(当前:vem-admin、admin-page-design;后续新子应用同规范)
现状与问题
以 vem-admin 为例(hiapi-cloud-vem/vem-admin):
| 产物 | 体积 | 说明 |
|---|---|---|
hiapi-cloud-vem-admin.umd.js | 2.3 MB | UMD 单文件,无代码分割 |
vem-admin.css | 400 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。
实施顺序建议
- 方案一(主应用挂 window + 子应用 external + 删 CSS)
- 方案三(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 KB | 404 KB |
| external vue+element-plus + 删重复CSS | 1,399 KB | 416 KB | 51 KB |
| 再 external icons-vue+vue-router+vue-i18n | 1,122 KB | 342 KB | 51 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 发布流程需配套改造。待上面三项落地后再评估。