Micro-Frontend Architecture in Practice: Module Federation, qiankun, and Custom Solutions — Selection and Implementation

技术架构(Updated May 12, 2026)

Micro-Frontends: The Cure for Monolithic Applications

When a single-repo frontend exceeds 500K lines of code, 5+ teams, and 10-minute builds—micro-frontends become inevitable.

Pain Point Micro-Frontend Solution
Slow builds Independent sub-app builds, incremental deployment
Team conflicts Independent repos, independent releases
Tech stack lock-in Each sub-app can choose its own tech stack
Tight coupling Communication via contracts, not direct references

1. Solution Comparison

Solution Isolation Communication Performance Complexity Tech Stack
iframe Perfect postMessage Poor Low Any
qiankun (JS Sandbox) Good props/eventBus Good Medium Any
Module Federation None Shared modules Excellent Medium Webpack 5+
single-spa Good Custom Good High Any
EMP None Shared modules Excellent Medium Webpack 5

2. iframe Approach: Simple but Limited

Implementation

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

Pros and Cons

Advantages Limitations
Perfect CSS/JS isolation Poor performance (each iframe is a separate process)
Naturally secure URL not synced, state lost on refresh
Zero migration cost Modals/overlays clipped by iframe boundaries
Any tech stack Communication limited to postMessage

Suitable scenarios: Third-party embeds, low-interaction needs, security isolation priority.


3. qiankun: Alibaba's JS Sandbox Solution

Main App Configuration

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',       // Preload
  sandbox: { strictStyleIsolation: true }, // Strict style isolation
  singular: false,       // Allow multiple sub-apps to mount simultaneously
});

Sub-App Adaptation

// Sub-app entry
let root = null;

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

// Export lifecycle
export async function bootstrap() {}
export async function mount(props) { render(props); }
export async function unmount() { root.unmount(); }

// Standalone mode
if (!window.__POWERED_BY_QIANKUN__) {
  render({});
}

JS Sandbox Principle

qiankun uses Proxy to intercept window operations:

const fakeWindow = new Proxy(window, {
  get(target, key) {
    // Prioritize sandbox value
    if (sandbox.hasOwnProperty(key)) return sandbox[key];
    return target[key];
  },
  set(target, key, value) {
    // Write to sandbox, not real window
    sandbox[key] = value;
    return true;
  },
});

Style Isolation

Mode Implementation Limitations
strictStyleIsolation Shadow DOM Modal styles lost, global styles don't penetrate
experimentalStyleIsolation CSS Scope prefix Dynamically inserted styles not handled

4. Module Federation: Webpack 5 Runtime Module Sharing

Basic Concept

App A (Host) ──remote load──→ Modules from App B (Remote)
     ↑                              ↑
  Shared deps                   Shared deps
  (react, lodash...)           (react, lodash...)

Remote Configuration (Providing Modules)

// 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 Configuration (Consuming Modules)

// 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' },
  },
})

Dynamically Loading Remote Components

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

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

Module Federation 2.0 (2026)

New Feature Description
Native ESM support No longer depends on Webpack runtime
Rspack compatibility 5-10x build speed improvement
Vite plugin @originjs/vite-plugin-federation
Type safety Automatic type inference for remote modules
Runtime dynamic registration No need to determine remote URL at build time

5. Communication Strategies

Option 1: CustomEvent

// Sub-app publishes
window.dispatchEvent(new CustomEvent('order:created', {
  detail: { orderId: '12345' },
}));

// Main app subscribes
window.addEventListener('order:created', (e) => {
  console.log('New order:', e.detail);
});

Option 2: Shared State (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);
  }
}

// Main app creates, passes to sub-apps via props
const store = new MicroStore();

Option 3: URL Synchronization

// Sub-app communicates via URL params
const params = new URLSearchParams(location.search);
const token = params.get('token');

// Main app listens for route changes
router.afterEach((to) => {
  // Notify sub-apps of route change
  store.setState('route', to.path);
});

6. Shared Dependency Management

Strategy Implementation Pros/Cons
externals + CDN All shared libs via CDN Simple but version locked
Module Federation shared Runtime version negotiation Flexible but complex config
Monorepo shared packages pnpm workspace Strong consistency but coupled
NPM private packages Publish to private registry Decoupled but requires publish flow

Module Federation Shared Best Practices

shared: {
  react: {
    singleton: true,        // Only load one version
    strictVersion: true,    // Error on version mismatch
    requiredVersion: deps.react,
    eager: false,           // Async load
  },
  lodash: {
    singleton: false,       // Allow multiple versions
    requiredVersion: deps.lodash,
  },
}

7. Deployment Strategy

Independent Deployment

CDN
├── /main/          → Main app (hash: a1b2c3)
├── /app-order/     → Order sub-app (hash: d4e5f6)
├── /app-user/      → User sub-app (hash: g7h8i9)
└── /shared/        → Shared resources (react, lodash...)

Version Management

// remote-config.json (loaded dynamically by main app)
{
  "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"
    }
  }
}

The main app fetches this config on startup, enabling independent deployment and canary releases for sub-apps.


8. Selection Decision Tree

Need perfect isolation?
  ├── Yes → iframe
  └── No → Same tech stack?
            ├── Yes → Module Federation
            └── No → Need sandbox isolation?
                      ├── Yes → qiankun
                      └── No → single-spa / custom

2026 Recommendation: For new projects, prioritize Module Federation 2.0 + Rspack for both performance and developer experience. Consider qiankun only when strong isolation is required.

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