前端微前端架构选型:Module Federation 与 qiankun 的对比实践

一、微前端的"选型焦虑":两种范式的根本分歧
微前端架构在前端领域已不再是新鲜概念,但选型仍然是团队面临的第一道难题。当前主流方案分为两大阵营:以 qiankun 为代表的"运行时容器"方案,和以 Module Federation 为代表的"构建时联邦"方案。某金融科技公司同时使用了两种方案——主应用用 qiankun 集成 3 个旧系统,新子系统用 Module Federation 互通——结果维护两套微前端基础设施的成本远超预期。
两种方案不是简单的"谁更好"的关系,而是解决不同问题的工具。理解其底层机制差异,是做出正确选型的前提。
二、两种微前端范式的架构对比
flowchart TB
subgraph MF["Module Federation 方案"]
MF_HOST[宿主应用] –> MF_REMOTE[远程模块]
MF_REMOTE –> MF_SHARED[共享依赖]
MF_SHARED –> MF_VUE[vue@3.x]
MF_SHARED –> MF_REACT[react@18.x]
MF_HOST -.->|构建时协商| MF_SHARED
end
subgraph QK["qiankun 方案"]
QK_MAIN[主应用] –> QK_SUB1[子应用A: Vue3]
QK_MAIN –> QK_SUB2[子应用B: React]
QK_MAIN –> QK_SUB3[子应用C: Angular]
QK_MAIN -.->|运行时加载| QK_SUB1
QK_MAIN -.->|JS 沙箱| QK_SUB2
QK_MAIN -.->|CSS 隔离| QK_SUB3
end
style MF fill:#dfd,stroke:#333
style QK fill:#ffd,stroke:#333
核心差异对比:
| 集成时机 | 构建时 | 运行时 |
| 隔离机制 | 模块作用域 | JS 沙箱 + CSS 隔离 |
| 框架限制 | 同构建工具即可 | 无限制 |
| 通信方式 | 模块导入 | 全局状态/CustomEvent |
| 技术栈要求 | 推荐 Webpack 5/Rspack | 无要求 |
| 适用场景 | 新系统间模块共享 | 旧系统集成 |
三、Module Federation 的生产级配置
// vite.config.ts — Module Federation 配置(使用 @originjs/vite-plugin-federation)
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import federation from '@originjs/vite-plugin-federation';
// ============ 远程模块配置(子应用) ============
export default defineConfig({
plugins: [
vue(),
federation({
name: 'remote-app',
filename: 'remoteEntry.js',
// 暴露给宿主应用的模块
exposes: {
'./UserList': './src/components/UserList.vue',
'./OrderPanel': './src/components/OrderPanel.vue',
'./useAuth': './src/composables/useAuth.ts',
'./utils': './src/utils/index.ts',
},
// 共享依赖配置
shared: {
vue: {
requiredVersion: '^3.3.0',
singleton: true, // 强制单例,避免多版本共存
eager: false, // 懒加载共享模块
},
'vue-router': {
singleton: true,
requiredVersion: '^4.2.0',
},
pinia: {
singleton: true,
requiredVersion: '^2.1.0',
},
},
}),
],
build: {
target: 'esnext',
minify: true,
cssCodeSplit: false,
},
});
// ============ 宿主应用配置 ============
// host-app/vite.config.ts
export default defineConfig({
plugins: [
vue(),
federation({
name: 'host-app',
// 引用远程模块
remotes: {
remoteApp: 'http://localhost:3001/assets/remoteEntry.js',
},
// 共享依赖(与远程模块对齐)
shared: {
vue: {
singleton: true,
requiredVersion: '^3.3.0',
},
'vue-router': {
singleton: true,
requiredVersion: '^4.2.0',
},
pinia: {
singleton: true,
requiredVersion: '^2.1.0',
},
},
}),
],
});
// ============ 宿主应用中使用远程模块 ============
// src/App.vue
<script setup lang="ts">
// 动态导入远程模块
const UserList = defineAsyncComponent(() =>
import('remoteApp/UserList')
);
const OrderPanel = defineAsyncComponent(() =>
import('remoteApp/OrderPanel')
);
// 导入远程 Composable
const { useAuth } = await import('remoteApp/useAuth');
const { user, isAuthenticated } = useAuth();
</script>
<template>
<div class="app">
<header v-if="isAuthenticated">
欢迎, {{ user.name }}
</header>
<main>
<Suspense>
<template #default>
<UserList />
</template>
<template #fallback>
<div class="loading">加载远程模块中…</div>
</template>
</Suspense>
<Suspense>
<template #default>
<OrderPanel />
</template>
<template #fallback>
<div class="loading">加载远程模块中…</div>
</template>
</Suspense>
</main>
</div>
</template>
四、qiankun 的生产级配置
// qiankun-config.ts — qiankun 微前端配置
import { registerMicroApps, start, addGlobalUncaughtErrorHandler } from 'qiankun';
// ============ 主应用配置 ============
interface MicroAppConfig {
name: string;
entry: string;
container: string;
activeRule: string;
props?: Record<string, any>;
}
const microApps: MicroAppConfig[] = [
{
name: 'vue3-sub-app',
entry: '//localhost:3002',
container: '#subapp-container',
activeRule: '/vue3',
props: {
// 传递给子应用的数据
mainAppToken: localStorage.getItem('token'),
},
},
{
name: 'react-sub-app',
entry: '//localhost:3003',
container: '#subapp-container',
activeRule: '/react',
},
];
registerMicroApps(microApps, {
// 子应用加载前
beforeLoad: [
(app) => {
console.log(`加载子应用: ${app.name}`);
return Promise.resolve();
},
],
// 子应用挂载后
afterMount: [
(app) => {
console.log(`子应用已挂载: ${app.name}`);
return Promise.resolve();
},
],
// 子应用卸载后
afterUnmount: [
(app) => {
console.log(`子应用已卸载: ${app.name}`);
return Promise.resolve();
},
],
});
// 全局错误处理
addGlobalUncaughtErrorHandler((event) => {
console.error('微前端全局错误:', event);
// 子应用加载失败时显示降级 UI
const container = document.getElementById('subapp-container');
if (container) {
container.innerHTML = '<div class="error-fallback">子应用加载失败,请刷新重试</div>';
}
});
// 启动 qiankun
start({
// 预加载策略
prefetch: 'all',
// JS 沙箱模式
sandbox: {
strictStyleIsolation: false, // 严格样式隔离(Shadow DOM)
experimentalStyleIsolation: true, // 实验性样式隔离(scope prefix)
},
// 全局状态通信
// 通过 initGlobalState 实现
});
// ============ 全局状态通信 ============
import { initGlobalState } from 'qiankun';
const { onGlobalStateChange, setGlobalState } = initGlobalState({
user: null,
token: '',
permissions: [],
});
// 主应用监听状态变化
onGlobalStateChange((state, prev) => {
console.log('全局状态变更:', state);
});
// 主应用设置状态(如登录后)
function onLoginSuccess(user, token) {
setGlobalState({
user,
token,
permissions: user.permissions,
});
}
// ============ 子应用生命周期(Vue3) ============
// vue3-sub-app/src/main.ts
let app: App | null = null;
function render(props: { container?: HTMLElement }) {
const container = props.container
? props.container.querySelector('#app')
: document.getElementById('app');
app = createApp(App);
app.use(router);
app.use(pinia);
app.mount(container);
}
// 独立运行
if (!(window as any).__POWERED_BY_QIANKUN__) {
render({});
}
// qiankun 生命周期
export async function bootstrap() {
console.log('子应用 bootstrap');
}
export async function mount(props: any) {
console.log('子应用 mount', props);
// 接收主应用传递的数据
if (props.onGlobalStateChange) {
props.onGlobalStateChange((state: any) => {
// 同步主应用状态到子应用 Pinia
const pinia = app?.config.globalProperties.$pinia;
if (pinia) {
pinia.state.value.auth = {
user: state.user,
token: state.token,
};
}
});
}
render(props);
}
export async function unmount() {
console.log('子应用 unmount');
app?.unmount();
app = null;
}
五、选型的 Trade-offs 与决策建议
Module Federation 的共享依赖风险。singleton: true 强制共享模块单例,但版本不兼容时会导致运行时错误。建议严格对齐共享依赖的 requiredVersion,并在 CI 中增加版本一致性检查。
qiankun 的沙箱性能开销。JS 沙箱(Proxy 代理)和 CSS 隔离(scope 前缀)会带来约 5-15% 的性能开销。对于性能敏感的应用(如数据可视化大屏),建议使用 Module Federation 的模块级隔离。
混合使用的维护成本。同时维护两套微前端基础设施的成本远超单一方案。建议根据团队技术栈统一选择一种方案——新系统用 Module Federation,旧系统集成用 qiankun,避免在同一项目中混用。
选型决策树:需要集成异构旧系统 → qiankun;新系统间模块共享 → Module Federation;需要严格隔离 → qiankun;需要细粒度模块共享 → Module Federation;团队统一 Webpack 5 → Module Federation;技术栈混杂 → qiankun。
五、总结
Module Federation 和 qiankun 是两种不同范式的微前端方案:前者基于构建时联邦实现模块级共享,后者基于运行时容器实现应用级隔离。Module Federation 适合新系统间的细粒度模块共享,qiankun 适合异构旧系统的集成。选型的核心依据是业务场景而非技术偏好——需要隔离选 qiankun,需要共享选 Module Federation。避免在同一项目中混用两种方案,统一技术栈是降低维护成本的关键。
