微前端之ModuleFederation与qiankun对比

387 阅读12分钟

微前端之ModuleFederation与qiankun对比

引言

随着前端应用规模的不断扩大和团队协作的复杂化,微前端架构逐渐成为大型前端项目的主流解决方案。在众多微前端技术方案中,Module Federation(模块联邦)和qiankun(乾坤)是两个最具代表性的技术方案。Module Federation基于Webpack 5的原生能力,提供了更现代化的模块共享机制;而qiankun基于single-spa,专注于应用级别的微前端管理。本文将从技术架构、实现原理、使用场景等多个维度深入对比这两种方案,提供选型参考。

1. 技术架构对比

1.1 Module Federation架构

Module Federation是Webpack 5推出的革命性特性,通过模块联邦实现跨应用的模块共享。

// 宿主应用配置 (Shell App)
const ModuleFederationPlugin = require('@module-federation/webpack');

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'shell',
      remotes: {
        mfRemote1: 'mfRemote1@http://localhost:3001/remoteEntry.js',
        mfRemote2: 'mfRemote2@http://localhost:3002/remoteEntry.js'
      },
      shared: {
        react: { singleton: true },
        'react-dom': { singleton: true }
      }
    })
  ]
};

// 远程应用配置 (Remote App)
module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'mfRemote1',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/components/Button',
        './App': './src/App'
      },
      shared: {
        react: { singleton: true },
        'react-dom': { singleton: true }
      }
    })
  ]
};

1.2 qiankun架构

qiankun基于single-spa构建,通过应用注册和生命周期管理实现微前端。

// 主应用配置
import { registerMicroApps, start } from 'qiankun';

registerMicroApps([
  {
    name: 'microApp1',
    entry: '//localhost:3001',
    container: '#container',
    activeRule: '/micro-app1'
  },
  {
    name: 'microApp2', 
    entry: '//localhost:3002',
    container: '#container',
    activeRule: '/micro-app2'
  }
], {
  beforeLoad: [
    app => console.log('before load', app.name)
  ],
  beforeMount: [
    app => console.log('before mount', app.name)
  ]
});

start();

// 子应用配置
export async function bootstrap() {
  console.log('microApp1 bootstraped');
}

export async function mount(props) {
  console.log('microApp1 mount', props);
  render(props.container);
}

export async function unmount(props) {
  console.log('microApp1 unmount', props);
  ReactDOM.unmountComponentAtNode(props.container);
}

2. 核心原理深度解析

2.1 Module Federation原理架构

graph TB
    subgraph "宿主应用 (Shell App)"
        A[应用启动] --> B[加载远程模块清单]
        B --> C[动态导入远程模块]
        C --> D[模块依赖解析]
        D --> E[共享模块管理]
    end
    
    subgraph "远程应用 (Remote App)"
        F[构建远程入口] --> G[暴露模块配置]
        G --> H[生成remoteEntry.js]
        H --> I[模块导出]
    end
    
    subgraph "运行时"
        J[模块联邦容器] --> K[模块缓存]
        K --> L[依赖版本管理]
        L --> M[模块实例化]
    end
    
    C --> H
    E --> K
    I --> M

2.2 Module Federation运行时机制

class ModuleFederationRuntime {
  constructor() {
    this.moduleCache = new Map();
    this.remoteContainers = new Map();
    this.sharedModules = new Map();
  }

  // 加载远程容器
  async loadRemoteContainer(remoteName, remoteUrl) {
    if (this.remoteContainers.has(remoteName)) {
      return this.remoteContainers.get(remoteName);
    }

    // 动态加载远程入口脚本
    await this.loadScript(remoteUrl);
    
    const container = window[remoteName];
    await container.init(__webpack_share_scopes__.default);
    
    this.remoteContainers.set(remoteName, container);
    return container;
  }

  // 获取远程模块
  async getRemoteModule(remoteName, modulePath) {
    const container = await this.loadRemoteContainer(remoteName);
    const factory = await container.get(modulePath);
    const module = factory();
    
    return module;
  }

  // 共享模块管理
  setupSharedScope(shared) {
    Object.entries(shared).forEach(([name, config]) => {
      const sharedModule = {
        ...config,
        loaded: false,
        get: () => this.getSharedModule(name)
      };
      this.sharedModules.set(name, sharedModule);
    });
  }

  loadScript(url) {
    return new Promise((resolve, reject) => {
      const script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = url;
      script.onload = resolve;
      script.onerror = reject;
      document.head.appendChild(script);
    });
  }
}

2.3 qiankun原理架构

graph TB
    subgraph "主应用 (Main App)"
        A[路由监听] --> B[匹配激活规则]
        B --> C[生命周期管理]
        C --> D[资源加载]
        D --> E[沙箱创建]
        E --> F[应用挂载]
    end
    
    subgraph "子应用 (Micro App)"
        G[导出生命周期] --> H[bootstrap]
        H --> I[mount]
        I --> J[unmount]
        J --> K[update]
    end
    
    subgraph "隔离机制"
        L[JavaScript沙箱] --> M[SnapshotSandbox]
        L --> N[ProxySandbox]
        O[样式隔离] --> P[Shadow DOM]
        O --> Q[CSS Scoped]
    end
    
    C --> H
    E --> L
    F --> I
    E --> O

2.4 qiankun沙箱机制实现

class ProxySandbox {
  constructor() {
    this.proxyWindow = {};
    this.isRunning = false;
    this.modifiedPropsMap = new Map();
    
    const proxy = new Proxy(this.proxyWindow, {
      get: (target, prop) => {
        if (prop === Symbol.unscopables) return target;
        return prop in target ? target[prop] : window[prop];
      },
      
      set: (target, prop, value) => {
        if (this.isRunning) {
          target[prop] = value;
          this.modifiedPropsMap.set(prop, value);
        }
        return true;
      },
      
      has: (target, prop) => {
        return prop in target || prop in window;
      }
    });
    
    this.proxy = proxy;
  }

  active() {
    this.isRunning = true;
  }

  inactive() {
    this.isRunning = false;
    // 清理修改的属性
    this.modifiedPropsMap.clear();
  }
}

class StyleSandbox {
  constructor() {
    this.dynamicStyleSheets = [];
  }

  patchDocumentCreateElement() {
    const originalCreateElement = document.createElement;
    const self = this;
    
    document.createElement = function(tagName, options) {
      const element = originalCreateElement.call(document, tagName, options);
      
      if (tagName.toLowerCase() === 'style') {
        self.dynamicStyleSheets.push(element);
      }
      
      return element;
    };
  }

  removeAllDynamicStyles() {
    this.dynamicStyleSheets.forEach(style => {
      if (style.parentNode) {
        style.parentNode.removeChild(style);
      }
    });
    this.dynamicStyleSheets = [];
  }
}

3. 应用生命周期管理

3.1 Module Federation动态加载

class MicroAppLoader {
  constructor() {
    this.loadedApps = new Map();
    this.appConfigs = new Map();
  }

  // 注册微前端应用
  registerApp(name, config) {
    this.appConfigs.set(name, {
      remoteName: config.remoteName,
      remoteUrl: config.remoteUrl,
      modulePath: config.modulePath,
      container: config.container,
      props: config.props || {}
    });
  }

  // 动态加载应用
  async loadApp(name) {
    const config = this.appConfigs.get(name);
    if (!config) {
      throw new Error(`App ${name} not registered`);
    }

    if (this.loadedApps.has(name)) {
      return this.loadedApps.get(name);
    }

    const runtime = new ModuleFederationRuntime();
    const AppComponent = await runtime.getRemoteModule(
      config.remoteName,
      config.modulePath
    );

    const appInstance = {
      component: AppComponent,
      config,
      mounted: false
    };

    this.loadedApps.set(name, appInstance);
    return appInstance;
  }

  // 挂载应用
  async mountApp(name, props = {}) {
    const app = await this.loadApp(name);
    
    if (app.mounted) {
      return;
    }

    const container = document.querySelector(app.config.container);
    if (!container) {
      throw new Error(`Container ${app.config.container} not found`);
    }

    // React 应用挂载示例
    const React = await import('react');
    const ReactDOM = await import('react-dom');
    
    ReactDOM.render(
      React.createElement(app.component.default, {
        ...app.config.props,
        ...props
      }),
      container
    );

    app.mounted = true;
  }

  // 卸载应用
  async unmountApp(name) {
    const app = this.loadedApps.get(name);
    if (!app || !app.mounted) {
      return;
    }

    const container = document.querySelector(app.config.container);
    if (container) {
      const ReactDOM = await import('react-dom');
      ReactDOM.unmountComponentAtNode(container);
    }

    app.mounted = false;
  }
}

3.2 qiankun应用管理增强

class EnhancedQiankun {
  constructor() {
    this.apps = new Map();
    this.globalState = this.createGlobalState();
    this.eventBus = new EventTarget();
  }

  // 增强的应用注册
  registerApp(appConfig) {
    const enhancedConfig = {
      ...appConfig,
      loader: this.createCustomLoader(appConfig),
      sandbox: {
        strictStyleIsolation: true,
        experimentalStyleIsolation: true
      },
      props: {
        ...appConfig.props,
        globalState: this.globalState,
        eventBus: this.eventBus
      }
    };

    this.apps.set(appConfig.name, enhancedConfig);
    return enhancedConfig;
  }

  // 自定义加载器
  createCustomLoader(appConfig) {
    return (loading) => {
      const loadingElement = document.querySelector(appConfig.container);
      if (loadingElement) {
        loadingElement.innerHTML = loading 
          ? '<div class="micro-app-loading">加载中...</div>'
          : '';
      }
    };
  }

  // 全局状态管理
  createGlobalState() {
    const state = {
      data: {},
      listeners: new Set()
    };

    return {
      set: (key, value) => {
        state.data[key] = value;
        state.listeners.forEach(listener => {
          listener({ type: 'SET', key, value, state: state.data });
        });
      },
      get: (key) => state.data[key],
      subscribe: (listener) => {
        state.listeners.add(listener);
        return () => state.listeners.delete(listener);
      }
    };
  }

  // 应用间通信
  broadcastMessage(type, data) {
    const event = new CustomEvent('micro-app-message', {
      detail: { type, data, timestamp: Date.now() }
    });
    this.eventBus.dispatchEvent(event);
  }

  // 错误边界处理
  setupErrorBoundary() {
    window.addEventListener('error', (event) => {
      console.error('Micro app error:', event.error);
      this.broadcastMessage('ERROR', {
        message: event.error.message,
        stack: event.error.stack,
        source: event.filename
      });
    });
  }
}

4. 性能优化与最佳实践

4.1 Module Federation优化策略

// 智能缓存策略
class ModuleFederationCache {
  constructor() {
    this.moduleCache = new Map();
    this.versionCache = new Map();
    this.preloadQueue = [];
  }

  // 预加载策略
  async preloadRemotes(remotes) {
    const preloadPromises = remotes.map(async (remote) => {
      const { name, url } = remote;
      
      // 检查版本更新
      if (await this.checkVersion(name, url)) {
        await this.loadRemoteContainer(name, url);
      }
    });

    await Promise.allSettled(preloadPromises);
  }

  // 版本检查
  async checkVersion(remoteName, remoteUrl) {
    try {
      const versionUrl = remoteUrl.replace('remoteEntry.js', 'version.json');
      const response = await fetch(versionUrl);
      const versionInfo = await response.json();
      
      const cachedVersion = this.versionCache.get(remoteName);
      if (!cachedVersion || cachedVersion !== versionInfo.version) {
        this.versionCache.set(remoteName, versionInfo.version);
        return true;
      }
      
      return false;
    } catch (error) {
      console.warn(`Version check failed for ${remoteName}:`, error);
      return true; // 默认加载
    }
  }

  // 模块懒加载
  async lazyLoadModule(remoteName, modulePath) {
    const cacheKey = `${remoteName}:${modulePath}`;
    
    if (this.moduleCache.has(cacheKey)) {
      return this.moduleCache.get(cacheKey);
    }

    const module = await this.loadRemoteModule(remoteName, modulePath);
    this.moduleCache.set(cacheKey, module);
    
    return module;
  }
}

// Webpack配置优化
const optimizedMFConfig = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'shell',
      remotes: {
        // 动态远程配置
        mfRemote: `promise new Promise(resolve => {
          const remoteUrl = getRemoteUrl();
          const script = document.createElement('script');
          script.src = remoteUrl;
          script.onload = () => {
            resolve(window.mfRemote);
          };
          document.head.appendChild(script);
        })`
      },
      shared: {
        react: { 
          singleton: true,
          eager: false,
          requiredVersion: '^18.0.0'
        },
        'react-dom': { 
          singleton: true,
          eager: false 
        }
      }
    })
  ],
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  }
};

4.2 qiankun性能优化

class QiankunOptimizer {
  constructor() {
    this.resourceCache = new Map();
    this.prefetchQueue = [];
    this.performanceObserver = this.setupPerformanceMonitor();
  }

  // 资源预取策略
  setupResourcePrefetch(apps) {
    const observer = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          const appName = entry.target.dataset.appName;
          this.prefetchApp(appName);
        }
      });
    });

    // 监听导航链接
    document.querySelectorAll('[data-app-name]').forEach(link => {
      observer.observe(link);
    });
  }

  async prefetchApp(appName) {
    const appConfig = this.getAppConfig(appName);
    if (!appConfig) return;

    try {
      // 预取HTML
      const html = await fetch(appConfig.entry).then(res => res.text());
      this.resourceCache.set(`${appName}:html`, html);

      // 解析并预取资源
      const resources = this.extractResources(html, appConfig.entry);
      await this.prefetchResources(resources);
      
    } catch (error) {
      console.warn(`Prefetch failed for ${appName}:`, error);
    }
  }

  extractResources(html, baseUrl) {
    const parser = new DOMParser();
    const doc = parser.parseFromString(html, 'text/html');
    const resources = [];

    // 提取CSS和JS资源
    doc.querySelectorAll('link[href], script[src]').forEach(element => {
      const url = element.getAttribute('href') || element.getAttribute('src');
      if (url && !url.startsWith('http')) {
        resources.push(new URL(url, baseUrl).href);
      }
    });

    return resources;
  }

  async prefetchResources(urls) {
    const prefetchPromises = urls.map(url => {
      return fetch(url, { mode: 'no-cors' }).catch(() => {
        // 忽略预取失败
      });
    });

    await Promise.allSettled(prefetchPromises);
  }

  // 性能监控
  setupPerformanceMonitor() {
    if ('PerformanceObserver' in window) {
      const observer = new PerformanceObserver((list) => {
        list.getEntries().forEach((entry) => {
          if (entry.name.includes('micro-app')) {
            console.log('Micro app performance:', entry);
          }
        });
      });

      observer.observe({ entryTypes: ['measure', 'navigation'] });
      return observer;
    }
  }

  // 内存优化
  cleanupInactiveApps() {
    const inactiveApps = this.getInactiveApps();
    
    inactiveApps.forEach(appName => {
      // 清理缓存
      this.resourceCache.delete(`${appName}:html`);
      
      // 触发垃圾回收提示
      if (window.gc && typeof window.gc === 'function') {
        window.gc();
      }
    });
  }
}

5. 编译时 vs 运行时:为什么Module Federation不需要沙箱

5.1 编译时隔离 vs 运行时隔离的本质差异

Module Federation和qiankun采用了截然不同的隔离策略,这个差异源于它们处理微前端的时机不同:

graph TB
    subgraph "Module Federation - 编译时处理"
        A[源码编译] --> B[依赖分析]
        B --> C[模块图构建]
        C --> D[Federation容器生成]
        D --> E[静态依赖解析]
        E --> F[运行时安全加载]
    end
    
    subgraph "qiankun - 运行时处理"
        G[应用加载] --> H[HTML解析]
        H --> I[脚本执行]
        I --> J[全局污染检测]
        J --> K[沙箱隔离]
        K --> L[动态环境管理]
    end
    
    subgraph "隔离机制对比"
        M[编译时隔离<br/>静态分析<br/>原生模块系统] 
        N[运行时隔离<br/>动态拦截<br/>Proxy沙箱]
    end
    
    F --> M
    L --> N

5.2 Module Federation的编译时隔离机制

// Module Federation在编译时就确定了模块边界
class CompileTimeIsolation {
  constructor() {
    this.federationConfig = {
      // 编译时确定的远程模块
      remotes: {
        'remote1': 'remote1@http://localhost:3001/remoteEntry.js'
      },
      // 编译时确定的共享依赖
      shared: {
        'react': {
          singleton: true,
          requiredVersion: '^18.0.0'
        }
      },
      // 编译时确定的暴露模块
      exposes: {
        './Button': './src/Button'
      }
    };
  }

  // 编译时生成的模块加载函数
  async loadRemoteModule(remoteName, moduleName) {
    // 这里的代码是Webpack在编译时生成的
    const container = await this.getContainer(remoteName);
    const factory = await container.get(moduleName);
    
    // 返回的是经过Webpack处理的标准ES模块
    return factory();
  }

  // 编译时确定的依赖管理
  resolveSharedDependency(depName) {
    // Webpack在编译时已经分析了所有依赖关系
    const sharedScope = __webpack_share_scopes__.default;
    return sharedScope[depName];
  }
}

// Webpack生成的Federation运行时(简化版)
const FederationRuntime = {
  // 编译时生成的模块映射
  moduleMap: {
    'remote1': {
      './Button': () => import('./remote1_Button.js')
    }
  },
  
  // 编译时确定的共享模块管理
  sharedModules: {
    'react': {
      version: '18.2.0',
      scope: 'default',
      get: () => () => require('react')
    }
  },
  
  // 运行时执行,但基于编译时分析
  async importRemote(remote, module) {
    const moduleFactory = this.moduleMap[remote][module];
    return await moduleFactory();
  }
};

5.3 为什么Module Federation不需要运行时沙箱

5.3.1 模块级别的天然隔离
// Module Federation中,每个远程模块都是独立的ES模块
// 远程应用A导出的组件
// remote-app-a/src/Button.js
export default function Button({ children, onClick }) {
  // 这个组件的作用域是模块级别的,天然隔离
  const [state, setState] = useState(false);
  
  return <button onClick={onClick}>{children}</button>;
}

// 宿主应用中使用
// shell-app/src/App.js
import RemoteButton from 'remoteA/Button'; // 编译时确定的导入

function App() {
  return (
    <div>
      {/* RemoteButton运行在自己的模块作用域中 */}
      <RemoteButton onClick={() => console.log('clicked')}>
        Remote Button
      </RemoteButton>
    </div>
  );
}
5.3.2 Webpack模块系统的内置隔离
// Webpack Federation生成的运行时模块隔离机制
class WebpackModuleIsolation {
  constructor() {
    // 每个Federation应用都有自己的模块缓存
    this.moduleCache = new Map();
    // 每个应用都有独立的模块图
    this.moduleGraph = new Map();
  }

  // Webpack确保模块ID不冲突
  generateModuleId(appName, modulePath) {
    return `${appName}__${modulePath}`;
  }

  // 模块加载时的作用域隔离
  loadModule(moduleId) {
    if (this.moduleCache.has(moduleId)) {
      return this.moduleCache.get(moduleId);
    }

    // 每个模块都在独立的函数作用域中执行
    const moduleFactory = this.moduleGraph.get(moduleId);
    const moduleScope = {};
    
    // Webpack包装的模块执行环境
    const moduleExports = moduleFactory.call(
      moduleScope,
      moduleScope, // module
      moduleScope, // exports
      this.requireFunction.bind(this) // require
    );
    
    this.moduleCache.set(moduleId, moduleExports);
    return moduleExports;
  }
}

5.4 qiankun的运行时沙箱必要性

// qiankun需要处理完整应用的全局污染问题
class RuntimeSandboxNecessity {
  constructor() {
    // 子应用可能修改的全局对象
    this.globalModifications = [
      'window.jQuery',
      'document.title', 
      'window.location',
      'document.body.style',
      'window.addEventListener'
    ];
  }

  // qiankun需要拦截所有可能的全局操作
  createApplicationSandbox(appName) {
    const originalWindow = window;
    const sandboxWindow = {};
    
    return new Proxy(sandboxWindow, {
      get(target, prop) {
        // 拦截所有属性访问
        if (prop in target) {
          return target[prop];
        }
        
        // 某些属性需要特殊处理
        if (prop === 'document') {
          return createDocumentProxy();
        }
        
        return originalWindow[prop];
      },
      
      set(target, prop, value) {
        // 拦截所有属性设置
        console.log(`App ${appName} setting ${prop}`);
        target[prop] = value;
        return true;
      }
    });
  }

  // 样式隔离也需要运行时处理
  createStyleIsolation(appName) {
    const originalCreateElement = document.createElement;
    
    document.createElement = function(tagName) {
      const element = originalCreateElement.call(document, tagName);
      
      if (tagName.toLowerCase() === 'style') {
        // 运行时添加应用标识
        element.setAttribute('data-qiankun', appName);
      }
      
      return element;
    };
  }
}

5.5 编译时与运行时处理的性能对比

// 性能测试对比
class PerformanceComparison {
  // Module Federation - 编译时优化
  static benchmarkModuleFederation() {
    const startTime = performance.now();
    
    // 1. 无需运行时解析HTML
    // 2. 无需运行时创建沙箱
    // 3. 直接使用原生ES模块加载
    const remoteModule = await import('remote/Component');
    
    const endTime = performance.now();
    return {
      type: 'Module Federation',
      loadTime: endTime - startTime,
      memoryOverhead: 'minimal', // 只有模块本身的内存开销
      runtimeOverhead: 'none'    // 无额外运行时开销
    };
  }

  // qiankun - 运行时处理
  static benchmarkQiankun() {
    const startTime = performance.now();
    
    // 1. 需要解析HTML内容
    const htmlContent = await fetch('/micro-app').then(r => r.text());
    
    // 2. 需要创建沙箱环境
    const sandbox = createProxySandbox();
    
    // 3. 需要样式隔离处理
    const styleIsolation = createStyleIsolation();
    
    // 4. 执行应用代码
    executeAppInSandbox(htmlContent, sandbox);
    
    const endTime = performance.now();
    return {
      type: 'qiankun',
      loadTime: endTime - startTime,
      memoryOverhead: 'significant', // 沙箱和隔离机制的内存开销
      runtimeOverhead: 'moderate'    // 运行时拦截和代理的开销
    };
  }
}

5.6 两种方案的适用场景分析

flowchart TD
    A[微前端隔离需求] --> B{应用粒度}
    
    B -->|模块级别| C[Module Federation<br/>编译时隔离]
    B -->|应用级别| D[qiankun<br/>运行时隔离]
    
    C --> E[优势:<br/>• 性能最优<br/>• 原生模块系统<br/>• 无运行时开销]
    C --> F[限制:<br/>• 需要Webpack 5<br/>• 模块粒度细<br/>• 构建时耦合]
    
    D --> G[优势:<br/>• 完整应用隔离<br/>• 技术栈无关<br/>• 独立部署]
    D --> H[限制:<br/>• 运行时开销<br/>• 复杂的沙箱机制<br/>• 内存占用更大]

5.7 隔离机制的深度对比

隔离维度Module Federationqiankun
JavaScript作用域ES模块天然隔离Proxy沙箱运行时隔离
CSS样式隔离需要CSS Modules等方案Shadow DOM/Scoped CSS
全局变量隔离模块作用域自动隔离运行时拦截和代理
DOM操作隔离组件级别的DOM操作应用级别的DOM沙箱
路由隔离需要自行管理内置路由匹配和隔离
存储隔离共享浏览器存储可配置存储隔离
事件隔离组件级别事件管理应用级别事件沙箱

这种根本性的差异使得Module Federation更适合组件级别的共享和复用,而qiankun更适合完整应用的集成和隔离。

6. 技术对比总结

6.1 全维度对比分析

对比维度Module Federationqiankun详细说明
技术基础Webpack 5原生支持single-spa + 沙箱机制MF基于构建工具,qiankun框架无关
处理时机编译时处理运行时处理核心差异:编译时vs运行时
学习成本中高,需要深度理解Webpack低,API简洁直观MF需要理解Federation配置
集成粒度模块/组件级别完整应用级别粒度决定使用场景
隔离机制ES模块天然隔离Proxy沙箱运行时隔离MF不需要沙箱的根本原因
路由管理需要自行实现内置路由匹配机制qiankun提供开箱即用的路由
构建要求必须使用Webpack 5+构建工具无关MF对构建工具有强依赖
运行时性能优秀,无额外开销良好,有沙箱开销编译时优化vs运行时开销
共享依赖原生支持,版本协商需要手动处理MF内置依赖管理机制
样式隔离需要CSS-in-JS/Modules内置多种隔离方案qiankun提供Shadow DOM等方案
开发体验类似本地开发需要配置应用生命周期MF开发体验更接近原生
部署复杂度中等,需要CDN支持较低,独立部署MF需要考虑远程资源可用性
错误隔离组件级别错误边界应用级别错误隔离错误影响范围不同
通信方式Props/Context/EventBus全局状态/事件总线通信机制的复杂度不同
版本管理支持多版本共存应用级别版本管理MF版本管理更细粒度
团队协作适合组件库团队适合业务应用团队团队结构影响技术选型
技术栈要求统一前端技术栈可异构技术栈qiankun支持Vue+React混用
首屏性能优秀,按需加载良好,需要优化MF天然支持代码分割
内存占用较低较高,沙箱开销运行时隔离的代价
调试难度中等,source map支持较高,跨应用调试调试体验差异明显

6.2 技术选型矩阵

6.2.1 基于场景的选择矩阵
应用场景优先推荐适用条件不适用条件
设计系统/组件库Module Federation• 统一技术栈
• 组件复用需求高
• 版本管理要求严格
• 需要完整应用隔离
• 技术栈异构
大型企业应用集成qiankun• 多个独立应用
• 技术栈多样
• 渐进式改造
• 主要是组件共享
• 性能要求极高
微服务前端化qiankun• 团队独立开发
• 部署独立
• 技术自主权
• 需要细粒度模块共享
• 团队技术栈统一
单仓库多包管理Module Federation• Monorepo架构
• 包级别共享
• 构建优化需求
• 跨域部署复杂
• 需要运行时动态加载
6.2.2 基于团队规模的选择
// 团队规模与技术选型的关系
const teamBasedSelection = {
  smallTeam: {
    size: '< 10人',
    recommendation: 'Module Federation',
    reason: '团队小,技术栈容易统一,组件共享收益大',
    considerations: [
      '学习成本可控',
      '维护复杂度较低',
      '性能优势明显'
    ]
  },
  
  mediumTeam: {
    size: '10-50人',
    recommendation: '视具体需求选择',
    reason: '需要根据具体业务场景和技术架构决策',
    considerations: [
      '多团队协作复杂度',
      '技术栈统一难度',
      '维护成本评估'
    ]
  },
  
  largeTeam: {
    size: '> 50人',
    recommendation: 'qiankun',
    reason: '大团队更需要独立性和隔离性',
    considerations: [
      '团队自主权重要',
      '技术栈选择灵活性',
      '独立部署和发布'
    ]
  }
};

6.3 选型决策流程图

flowchart TD
    A[微前端技术选型] --> B{是否使用Webpack 5+}
    B -->|是| C{主要需求是模块共享?}
    B -->|否| D[推荐 qiankun]
    
    C -->|是| E[推荐 Module Federation]
    C -->|否| F{需要完善的沙箱隔离?}
    
    F -->|是| D
    F -->|否| G{团队技术水平}
    
    G -->|高| H[可选择 Module Federation]
    G -->|中等| D
    
    E --> I[优势:原生性能,模块粒度细]
    D --> J[优势:生态成熟,上手简单]
    H --> K[需要自行实现路由和沙箱]

6. 实际应用场景

6.1 Module Federation适用场景

// 设计系统组件共享
const designSystemConfig = {
  name: 'designSystem',
  filename: 'remoteEntry.js',
  exposes: {
    './Button': './src/components/Button',
    './Input': './src/components/Input',
    './Modal': './src/components/Modal',
    './Theme': './src/theme/index'
  },
  shared: {
    react: { singleton: true },
    'styled-components': { singleton: true }
  }
};

// 业务功能模块共享
const businessModuleConfig = {
  name: 'userModule',
  exposes: {
    './UserProfile': './src/pages/UserProfile',
    './UserService': './src/services/userService',
    './UserStore': './src/store/userStore'
  }
};

6.2 qiankun适用场景

// 大型企业级应用集成
const enterpriseApps = [
  {
    name: 'crm-system',
    entry: '//crm.company.com',
    container: '#subapp-viewport',
    activeRule: '/crm'
  },
  {
    name: 'erp-system', 
    entry: '//erp.company.com',
    container: '#subapp-viewport',
    activeRule: '/erp'
  },
  {
    name: 'analytics-dashboard',
    entry: '//analytics.company.com', 
    container: '#subapp-viewport',
    activeRule: '/analytics'
  }
];

// 渐进式迁移场景
const migrationConfig = {
  // 主应用保持现有技术栈
  mainApp: {
    framework: 'Vue 2',
    router: 'vue-router'
  },
  // 新功能使用新技术栈
  microApps: [
    {
      name: 'new-feature',
      framework: 'React 18',
      entry: '/micro-apps/new-feature'
    }
  ]
};

7. 最佳实践指南

7.1 Module Federation最佳实践

// 1. 版本管理策略
const versionStrategy = {
  shared: {
    react: {
      singleton: true,
      strictVersion: true,
      requiredVersion: '^18.0.0',
      shareScope: 'default'
    }
  },
  // 使用语义化版本
  exposes: {
    './ComponentV1': './src/components/Button/v1',
    './ComponentV2': './src/components/Button/v2'
  }
};

// 2. 错误边界处理
class MicroAppErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, errorInfo: null };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    console.error('Micro app error:', error);
    this.setState({ errorInfo });
    
    // 上报错误
    this.reportError(error, errorInfo);
  }

  reportError(error, errorInfo) {
    // 错误上报逻辑
  }

  render() {
    if (this.state.hasError) {
      return (
        <div className="error-fallback">
          <h2>模块加载失败</h2>
          <button onClick={() => window.location.reload()}>
            重新加载
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

// 3. 动态导入优化
const DynamicMicroApp = React.lazy(async () => {
  try {
    const module = await import('mfRemote/App');
    return module;
  } catch (error) {
    console.error('Failed to load micro app:', error);
    // 返回降级组件
    return { default: () => <div>服务暂时不可用</div> };
  }
});

7.2 qiankun最佳实践

// 1. 全局错误处理
import { addGlobalUncaughtErrorHandler } from 'qiankun';

addGlobalUncaughtErrorHandler((event) => {
  console.error('Micro app error:', event);
  
  const { message, filename, lineno, colno, error } = event;
  
  // 错误上报
  reportError({
    message,
    filename, 
    lineno,
    colno,
    stack: error?.stack,
    userAgent: navigator.userAgent,
    timestamp: Date.now()
  });
});

// 2. 样式隔离增强
const styledIsolationConfig = {
  sandbox: {
    strictStyleIsolation: true,
    experimentalStyleIsolation: true
  },
  // 自定义样式处理
  beforeMount: (app) => {
    // 添加应用特定的CSS类
    document.body.classList.add(`micro-app-${app.name}`);
  },
  beforeUnmount: (app) => {
    // 清理应用特定的CSS类
    document.body.classList.remove(`micro-app-${app.name}`);
  }
};

// 3. 通信机制优化
class MicroAppCommunicator {
  constructor() {
    this.messageHandlers = new Map();
    this.globalState = this.initGlobalState({});
  }

  // 类型安全的消息通信
  sendMessage(targetApp, type, payload) {
    const message = {
      id: this.generateMessageId(),
      type,
      payload,
      timestamp: Date.now(),
      source: this.getCurrentApp()
    };

    this.globalState.setGlobalState({
      [`message:${targetApp}`]: message
    });
  }

  // 订阅消息
  onMessage(type, handler) {
    if (!this.messageHandlers.has(type)) {
      this.messageHandlers.set(type, new Set());
    }
    this.messageHandlers.get(type).add(handler);

    // 返回取消订阅函数
    return () => {
      this.messageHandlers.get(type)?.delete(handler);
    };
  }

  // 广播消息
  broadcast(type, payload) {
    const message = {
      type,
      payload,
      broadcast: true,
      timestamp: Date.now()
    };

    this.globalState.setGlobalState({
      broadcast: message
    });
  }
}

8. 总结与建议

微前端技术选型需要综合考虑团队技术栈、项目复杂度、性能要求等多个因素:

选择Module Federation的场景:

  • 团队已深度使用Webpack 5+
  • 主要需求是组件/模块级别的共享
  • 对运行时性能有较高要求
  • 团队有较强的前端工程化能力

选择qiankun的场景:

  • 需要集成多个独立的前端应用
  • 团队技术栈多样化
  • 需要渐进式改造现有系统
  • 希望快速上手微前端架构

无论选择哪种方案,都需要在团队协作、代码规范、部署策略、监控体系等方面建立完善的工程化体系,确保微前端架构能够真正提升团队效率和产品质量。