微前端架構實戰: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#架构