一、结论
css
是否需要 UMD,取决于 Entry 方式
JS Entry → 需要特殊打包格式(UMD/SystemJS/ESM)
HTML Entry → 不需要特殊格式
根本原因:
- JS Entry:主应用直接加载子应用的 JS 模块,需要访问子应用导出的生命周期函数
- HTML Entry :主应用加载子应用的 HTML 文件,自动解析
<script>标签,JS 正常执行
二、为什么 JS Entry 需要 UMD
JS Entry 的工作原理
javascript
// single-spa 的典型用法
registerApplication({
name: "sub-app",
app: () => System.import("http://localhost:3000/main.js"), // 直接加载 JS
activeWhen: "/sub-app",
});
// 主应用需要调用子应用的函数
const app = await loadApp("sub-app");
await app.mount(props); // ← 必须能访问到 mount 函数
UMD 提供的能力
UMD (Universal Module Definition) 让同一个 JS 文件在多种模块系统中都能被访问:
javascript
// UMD 包装器(简化版)
(function (root, factory) {
if (typeof define === "function" && define.amd) {
define([], factory); // AMD
} else if (typeof module === "object" && module.exports) {
module.exports = factory(); // CommonJS
} else {
root.subApp = factory(); // 浏览器全局变量
}
})(this, function () {
return {
bootstrap: () => {},
mount: () => {},
unmount: () => {},
};
});
关键点:
- ✅ 通过
<script>标签加载后,自动挂载到window.subApp - ✅ 主应用可以通过全局变量访问子应用的生命周期函数
- ✅ 跨模块系统兼容(AMD/CJS/全局变量)
三、为什么 HTML Entry 不需要 UMD
HTML Entry 的工作原理
javascript
// qiankun 的典型用法
registerMicroApps([
{
name: "sub-app",
entry: "//localhost:3000", // 加载 HTML
container: "#container",
activeRule: "/sub-app",
},
]);
qiankun 内部执行:
javascript
// 1. 请求 HTML
const html = await fetch("http://localhost:3000").then((res) => res.text());
// 2. 解析 HTML,提取 <script src="main.js">
const scripts = parseScripts(html);
// 3. 动态插入到页面
const scriptEl = document.createElement("script");
scriptEl.src = "main.js";
document.head.appendChild(scriptEl); // JS 自动执行
// ✅ 子应用正常启动,无需导出任何东西
为什么不需要 UMD
| 方面 | JS Entry | HTML Entry |
|---|---|---|
| 加载方式 | import() 或 System.import() |
<script src> |
| 模块访问 | 需要访问导出的对象 | JS 自动执行,无需访问 |
| 生命周期 | 子应用显式导出 | 框架自动拦截 |
| 打包格式 | UMD/SystemJS | 普通 SPA |
子应用代码(HTML Entry):
javascript
// 完全正常的应用代码,无需导出
import { createApp } from "vue";
createApp(App).mount("#app");
四、各框架对比
| 框架 | Entry 方式 | 是否需要 UMD | 说明 |
|---|---|---|---|
| single-spa | JS Entry | ✅ 必须 | 直接加载 JS 模块 |
| qiankun | HTML Entry | ❌ 不需要 | 自动解析 HTML |
| micro-app 默认 | HTML Entry | ❌ 不需要 | 自动解析 HTML |
| micro-app UMD模式 | 混合 | ✅ 推荐 | 性能优化方案 |
| Module Federation | 远程模块 | ❌ 不需要 | 现代替代方案 |
五、micro-app UMD 模式的特殊性
两种模式对比
| 模式 | Entry 方式 | 执行流程 | 性能 |
|---|---|---|---|
| 默认模式 | HTML Entry | 每次都执行完整 JS | 一般 |
| UMD 模式 | 混合 | 首次完整执行,后续只调 mount/unmount | 更好 |
UMD 模式的性能优化原理
默认模式:
javascript
// 每次渲染
第一次渲染:初始化 Vue → 挂载
第二次渲染:初始化 Vue → 挂载 ← 重复!
第三次渲染:初始化 Vue → 挂载 ← 重复!
UMD 模式:
javascript
// 子应用改造
let app = null
window.microApp = {
mount: () => {
if (!app) {
app = createApp(App) // 只初始化一次
}
app.mount('#app')
},
unmount: () => {
app.unmount()
}
}
// 执行流程
第一次渲染:初始化 → 调用 mount()
第二次渲染:直接调用 mount() ← 不重复初始化!
第三次渲染:直接调用 mount() ← 不重复初始化!
本质:混合方案(首次 HTML Entry + 后续 JS Entry)
六、常见误区
误区一:"微前端必须用 UMD"
错误。是否需要 UMD 取决于 Entry 方式:
- qiankun、micro-app 默认模式 → ❌ 不需要
- single-spa、micro-app UMD 模式 → ✅ 需要
误区二:"micro-app 的 UMD 模式就是打包成 UMD 格式"
错误 。micro-app 的 "UMD 模式" 指的是导出生命周期函数的模式,核心约定是:
javascript
window.microApp = { mount, unmount };
打包格式可以是 UMD,也可以是其他格式。
误区三:"ESM 可以完全替代 UMD"
不完全正确。虽然 ESM 是现代标准,但在微前端 JS Entry 场景中:
- UMD 提供最佳兼容性(全局变量访问)
- SystemJS 在 single-spa 生态中更常见
- ESM 在 Module Federation 中成为标准