微前端架构实战:Module Federation、qiankun 与自研方案的选型与落地

技术架构(更新于 2026年5月12日)

微前端:巨石应用的解药

当单仓库前端代码超过 50 万行、团队超过 5 个、构建超过 10 分钟——微前端是必然选择。

痛点 微前端的解法
构建慢 子应用独立构建,增量部署
团队冲突 子应用独立仓库,独立发布
技术栈锁定 每个子应用可选技术栈
耦合严重 共享通过契约,而非直接引用

一、方案对比

方案 隔离性 通信 性能 复杂度 技术栈
iframe 完美 postMessage 任意
qiankun (JS 沙箱) props/eventBus 任意
Module Federation 共享模块 Webpack 5+
single-spa 自定义 任意
EMP 共享模块 Webpack 5

二、iframe 方案:简单但受限

实现

<iframe
  src="https://sub-app.example.com"
  sandbox="allow-scripts allow-same-origin allow-forms"
  style="width: 100%; height: 100%; border: none;"
></iframe>

优势与局限

优势 局限
完美 CSS/JS 隔离 性能差(每个 iframe 独立进程)
天然安全 URL 不同步,刷新丢失状态
零改造成本 弹窗/遮罩层被 iframe 裁剪
任意技术栈 通信只能用 postMessage

适用场景:第三方嵌入、低交互需求、安全隔离优先。


三、qiankun:阿里出品的 JS 沙箱方案

主应用配置

import { registerMicroApps, start } from 'qiankun';

registerMicroApps([
  {
    name: 'app-order',
    entry: '//localhost:8081',
    container: '#subapp-container',
    activeRule: '/order',
    props: { mainStore, userInfo },
  },
  {
    name: 'app-user',
    entry: '//localhost:8082',
    container: '#subapp-container',
    activeRule: '/user',
    props: { mainStore, userInfo },
  },
]);

start({
  prefetch: 'all',       // 预加载
  sandbox: { strictStyleIsolation: true }, // 严格样式隔离
  singular: false,       // 允许多个子应用同时挂载
});

子应用改造

// 子应用入口
let root = null;

function render(props) {
  const { container, mainStore } = props;
  root = createApp(App);
  root.mount(container ? container.querySelector('#app') : '#app');
}

// 导出生命周期
export async function bootstrap() {}
export async function mount(props) { render(props); }
export async function unmount() { root.unmount(); }

// 独立运行
if (!window.__POWERED_BY_QIANKUN__) {
  render({});
}

JS 沙箱原理

qiankun 使用 Proxy 拦截 window 操作:

const fakeWindow = new Proxy(window, {
  get(target, key) {
    // 优先从沙箱取值
    if (sandbox.hasOwnProperty(key)) return sandbox[key];
    return target[key];
  },
  set(target, key, value) {
    // 写入沙箱而非真实 window
    sandbox[key] = value;
    return true;
  },
});

样式隔离

模式 实现 局限
strictStyleIsolation Shadow DOM 弹窗样式丢失、全局样式不穿透
experimentalStyleIsolation CSS Scope 前缀 动态插入的样式不处理

四、Module Federation:Webpack 5 的运行时模块共享

基本概念

App A (Host) ──远程加载──→ App B (Remote) 的模块
     ↑                          ↑
  共享依赖                    共享依赖
  (react, lodash...)         (react, lodash...)

Remote 配置(提供模块)

// webpack.config.js (App B - Remote)
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'appOrder',
      filename: 'remoteEntry.js',
      exposes: {
        './OrderList': './src/components/OrderList',
        './OrderDetail': './src/pages/OrderDetail',
      },
      shared: {
        react: { singleton: true, requiredVersion: '^18.0.0' },
        'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
      },
    }),
  ],
};

Host 配置(消费模块)

// webpack.config.js (App A - Host)
new ModuleFederationPlugin({
  name: 'appMain',
  remotes: {
    appOrder: 'appOrder@http://localhost:8081/remoteEntry.js',
  },
  shared: {
    react: { singleton: true, requiredVersion: '^18.0.0' },
    'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
  },
})

动态加载远程组件

const OrderList = React.lazy(() => import('appOrder/OrderList'));

function App() {
  return (
    <Suspense fallback={<Loading />}>
      <OrderList />
    </Suspense>
  );
}

Module Federation 2.0 (2026)

新特性 说明
原生 ESM 支持 不再依赖 Webpack runtime
Rspack 兼容 构建速度 5-10x 提升
Vite 插件 @originjs/vite-plugin-federation
类型安全 远程模块类型自动推导
运行时动态注册 无需构建时确定 remote URL

五、通信方案

方案一:CustomEvent

// 子应用发布
window.dispatchEvent(new CustomEvent('order:created', {
  detail: { orderId: '12345' },
}));

// 主应用订阅
window.addEventListener('order:created', (e) => {
  console.log('新订单:', e.detail);
});

方案二:共享状态(Micro Store)

class MicroStore {
  state = {};
  listeners = new Map();

  setState(key, value) {
    this.state[key] = value;
    this.listeners.get(key)?.forEach(fn => fn(value));
  }

  subscribe(key, listener) {
    if (!this.listeners.has(key)) this.listeners.set(key, new Set());
    this.listeners.get(key).add(listener);
    return () => this.listeners.get(key).delete(listener);
  }
}

// 主应用创建,通过 props 传递给子应用
const store = new MicroStore();

方案三:URL 同步

// 子应用通过 URL 参数通信
const params = new URLSearchParams(location.search);
const token = params.get('token');

// 主应用监听路由变化
router.afterEach((to) => {
  // 通知子应用路由变化
  store.setState('route', to.path);
});

六、公共依赖管理

策略 实现 优劣势
externals + CDN 所有公共库走 CDN 简单但版本锁定
Module Federation shared 运行时版本协商 灵活但配置复杂
Monorepo 共享包 pnpm workspace 强一致但耦合
NPM 私有包 发布到私有 registry 解耦但需发布流程

Module Federation shared 配置最佳实践

shared: {
  react: {
    singleton: true,        // 只加载一个版本
    strictVersion: true,    // 版本不匹配时报错
    requiredVersion: deps.react,
    eager: false,           // 异步加载
  },
  lodash: {
    singleton: false,       // 允许多版本
    requiredVersion: deps.lodash,
  },
}

七、部署策略

独立部署

CDN
├── /main/          → 主应用 (hash: a1b2c3)
├── /app-order/     → 订单子应用 (hash: d4e5f6)
├── /app-user/      → 用户子应用 (hash: g7h8i9)
└── /shared/        → 共享资源 (react, lodash...)

版本管理

// remote-config.json(主应用动态加载)
{
  "apps": {
    "app-order": {
      "url": "https://cdn.example.com/app-order/d4e5f6/remoteEntry.js",
      "version": "2.3.1"
    },
    "app-user": {
      "url": "https://cdn.example.com/app-user/g7h8i9/remoteEntry.js",
      "version": "1.8.0"
    }
  }
}

主应用启动时拉取此配置,实现子应用独立部署、灰度发布。


八、选型决策树

需要完美隔离?
  ├── 是 → iframe
  └── 否 → 同一技术栈?
            ├── 是 → Module Federation
            └── 否 → 需要沙箱隔离?
                      ├── 是 → qiankun
                      └── 否 → single-spa / 自研

2026 年推荐:新项目优先考虑 Module Federation 2.0 + Rspack,兼顾性能与开发体验。需要强隔离再考虑 qiankun。

#微前端#Module Federation#qiankun#架构