微前端系列:主流前端框架选型与对比

通过前面三篇文章,我们已经介绍了微前端的核心概念、路由与加载原理、隔离与通信机制。这篇文章,聚焦微前端落地的关键环节:框架选型。

本文将深度解析当前主流的微前端方案,从技术特点、核心优势、适用场景等维度做全面对比。

一、先明确:框架选型的核心维度

在对比具体框架之前,需要先建立统一的选型标准。一个合适的微前端框架,需要从以下几个维度评估:

  • 易用性:是否开箱即用、文档是否完善、集成主/子应用的配置步骤复杂度。对团队而言,低上手成本意味着更快的落地速度。
  • 兼容性:是否支持多种前端技术栈、是否兼容低版本浏览器、是否支持遗留系统。兼容性是微前端框架落地的前提。
  • 性能:子应用加载速度、资源重复加载情况、沙箱隔离的性能损耗、运行时的内存占用。性能直接影响用户体验。
  • 生态:生态是否成熟、社区活跃度、是否有大型企业背书。生态是衡量框架长期维护能力的重要指标。
  • 团队匹配度:框架的技术依赖是否与团队现有技术栈兼容。

二、主流框架深度解析

接下来,逐一拆解 Single-Spa、Qiankun、Module Federation/EMP、Micro App、wujie 的主要特点及核心机制实现方式。

框架 易用性 兼容性 性能 生态 综合特点
Single-SPA ⭐️⭐️ ⭐️⭐️⭐️⭐️ ⭐️⭐️⭐️ ⭐️⭐️⭐️⭐️ 灵活但需自研上层能力
Qiankun ⭐️⭐️⭐️⭐️⭐️ ⭐️⭐️⭐️⭐️⭐️ ⭐️⭐️⭐️ ⭐️⭐️⭐️⭐️⭐️ 开箱即用,强隔离,中后台首选
EMP ⭐️⭐️⭐️⭐️ ⭐️⭐️⭐️ ⭐️⭐️⭐️⭐️⭐️ ⭐️⭐️⭐️ Module Federation 的企业封装
Micro App ⭐️⭐️⭐️⭐️ ⭐️⭐️⭐️ ⭐️⭐️⭐️ ⭐️⭐️ Vue 友好,轻量但生态弱
Wujie ⭐️⭐️⭐️⭐️⭐️ ⭐️⭐️⭐️⭐️⭐️ ⭐️⭐️⭐️⭐️ ⭐️⭐️⭐️⭐️ 零侵入、强隔离、保活支持,新锐方案

1. Single-SPA

Single-SPA 是 2016 年诞生的微前端框架,也是微前端概念的最早实践者。

♠️ 核心定位是"微前端路由调度器",只负责子应用的注册、路由匹配和生命周期管理,不提供沙箱隔离、样式隔离等附加功能。

核心机制实现
路由分发

通过一个顶层路由器来管理各个微应用的路由。这个路由器监听 URL 的变化,并根据路由规则决定加载哪个微应用。

► 基于原生浏览器事件 popstatehashchange 来实现的,同时也会监听 pushStatereplaceState 的调用。

► 每个微应用需要暴露三个生命周期函数:bootstrapmountunmount。当路由匹配时,Single-SPA 会加载对应的微应用,并依次调用其生命周期函数。

► 路由规则是通过在注册微应用时提供的 activityFunction 来定义的。这个函数会根据当前的 URL(location)返回一个布尔值,表示该微应用是否应该被激活。

javascript 复制代码
singleSpa.registerApplication(
  "appName",  // 子应用名称
  () => System.import("appName"), // 加载函数,返回一个应用或者一个Promise
  (location) => location.pathname.startsWith("appName"), // 激活函数,返回一个布尔值,表示该应用是否应该被激活
  customProps: {
    // 自定义属性,会被传递给子应用的 bootstrap、mount 和 unmount 函数
  }
);

activityFunction 是一个纯函数,window.location 会作为第一个参数被调用,当函数返回的值为真(truthy)值时,应用会被激活。通常情况下,Activity function 会根据 window.location/后面的 path 来决定该应用是否需要被激活。

加载机制

本身并不直接提供加载微应用的功能,而是依赖于SystemJSWebpack 等模块加载器。但是,它规定了微应用必须被包装成一个对象,该对象包含生命周期函数。

► 当路由匹配时,Single-SPA 会调用注册应用时提供的加载函数(如() => System.import('app1'))来获取微应用的导出模块

► 获取到微应用导出的生命周期函数后,Single-SPA 会缓存这些函数,并在适当的时机(如路由变化时)调用它们

隔离机制

并没有内置的 JS 沙箱机制、也没有自动的 CSS 隔离机制。

通信机制

没有内置的通信机制,需要手动实现。

优缺点总结

✔️ 优势:灵活性极高,可定制化程度强,适合需要深度自定义微前端体系的场景;支持所有技术栈,兼容性好。

❗️ 缺点:上手成本高,配置繁琐,需手动解决隔离、通信等问题;无法"开箱即用"。

典型适用场景

需要高度自定义微前端架构、兼容多种小众技术栈的项目。

2. Qiankun

Qiankun 是阿里开源的微前端框架,基于 Single-SPA 封装。

核心定位是"企业级微前端解决方案",补全了 Single-SPA 缺失的沙箱隔离、样式隔离、静态资源加载等核心功能。

核心机制实现
路由分发

支持 hash 和 history 两种模式。支持精确匹配、前缀匹配等多种激活规则,可配置路由守卫。

  • history 模式通过重写 pushState/replaceState 方法+监听 popstate 事件实现路由拦截;
  • hash 模式通过监听 hashchange 事件实现;

► 注册微应用的基础配置信息。

► 当浏览器 url 发生变化时,会自动检查每一个微应用注册的 activeRule 规则,符合规则的应用将会被自动激活。

javascript 复制代码
registerMicroApps([
  {
    name: "app1", // 子应用名称
    entry: "//localhost:3001", // 子应用入口
    activeRule: "/app1", // 激活规则
    props: {
      // 自定义属性
    },
  },
]);
加载机制

采用"HTML 入口加载"模式,主应用通过 fetch 请求子应用入口 HTML,解析 HTML 中的 JS/CSS 资源并动态加载;内置资源预加载机制,可配置 preloadApps 参数在浏览器空闲时预加载子应用资源;支持依赖共享,避免同一依赖重复加载。

► 加载子应用资源:通过 fetch 请求子应用的入口文件(HTML 或 JS)。

► 解析入口文件:如果是 HTML 格式,则解析其中的 JS 和 CSS 资源,然后加载。

► 执行子应用代码:在沙箱环境中执行子应用的 JS 代码,获取其导出生命周期函数。

► 挂载子应用:将子应用挂载到指定的容器节点上。

预加载机制

qiankun 提供了预加载子应用资源的功能,可以在浏览器空闲时加载子应用的静态资源

javascript 复制代码
import { prefetchApps } from "qiankun";

prefetchApps([{ name: "app1", entry: "//localhost:3001" }]);
隔离机制

JS 沙箱:

  • 开发环境默认使用 Proxy 沙箱(为每个子应用创建 window 代理,属性读写隔离);
  • 降级使用 快照沙箱(记录挂载前后 window 差异,卸载时还原)。

CSS 隔离:

  • 基础模式:子应用挂载时插入其 CSS,卸载时自动移除;
  • 严格模式(strictStyleIsolation: true):启用 Shadow DOM 实现强隔离(兼容性有限)。
通信机制
  • Props 传递:主应用向子应用传入数据或方法;
  • Global State:通过 initGlobalState() 创建全局状态,支持跨应用监听变更;
  • 也可结合自定义事件总线(如 mitt)实现更复杂通信。
优缺点总结

✔️ 优势:开箱即用,配置简单;功能全面,覆盖企业级落地的核心需求;

❗️ 缺点:依赖 Single-SPA(自定义扩展需遵循框架规范)。

典型适用场景

大型中后台项目,特别是遗留系统重构(兼容旧技术栈)。

3.EMP

Module Federation 是 Webpack5 原生的"模块共享"方案。

EMP2.0 基于 Webpack5 的"微前端+模块联邦"方案,新版 EMP3.0 从 webpack 切换到 rspack(基于 Rust 的高性能构建引擎),来构建 JS。

Module Federation 核心定位是"跨应用模块共享",通过将不同应用的模块打包成"远程模块",实现应用间的模块复用和动态加载。++它并非传统意义上的微前端框架,而是从构建层面解决微前端的模块共享问题。++

EMP 核心基于 Module Federation 实现,定位为"企业级微前端模块联邦方案",通过封装 构建配置、路由系统、状态管理等能力,解决了 Module Federation 原生功能单一的问题,实现了"模块共享+微前端应用集成"的一体化解决方案

核心机制实现(EMP)
路由系统

基于 Module Federation,路由由主应用统一管理,子应用作为远程组件嵌入,无独立路由控制权。

加载机制

封装 Module Federation 配置,通过 CLI 自动生成 federated 构建配置,运行时动态加载远程模块。

继承 Module Federation 的"远程模块加载"核心,同时扩展了"应用级加载"能力。主应用通过 EMP 配置的 remotes 参数加载子应用远程模块;内置依赖共享优化,自动识别并共享公共依赖;支持按路由预加载子应用模块,提升页面切换速度;提供构建层面的资源优化,如代码分割、tree-shaking

隔离机制

无运行时隔离,依赖 MF 本身的模块边界和工程规范变更监听;

通信机制

通过共享模块(如状态管理库)通信

优缺点总结

✔️ 优势:开箱即用,性能优异;

❗️ 缺点:依赖 MF(Webpack5/3.0 rspack),对旧项目兼容性较差;社区文档、解决方案较少,问题排查成本高。

典型适用场景

主打 "组件 / 模块复用",适合需要共享通用组件、工具库的场景。实际项目中,可根据需求组合使用:用 EMP 实现模块 / 组件复用,用 qiankun 实现完整应用的嵌入。

4. Micro App

京东开源的"组件化微前端"方案

借鉴了 WebComponent 的思想,通过 js 沙箱、样式隔离、元素隔离、路由隔离模拟实现了 ShadowDom 的隔离特性,并结合 CustomElement 将微前端封装成一个类 WebComponent 组件,从而实现微前端的组件化渲染。

核心技术实现
路由分发

通过拦截浏览器路由事件以及自定义的 location、history,实现了一套虚拟路由系统,子应用运行在这套虚拟路由系统中,和主应用的路由进行隔离,避免相互影响。

虚拟路由系统分为四种模式:search、native、native-scope、pure

► search 模式:通子应用的路由信息会作为 query 参数同步到浏览器地址上;

► native 模式:直接使用浏览器的路由系统,子应用的路由完全由主应用管理;

► native-scope 模式:在 native 模式基础上,通过 URL 路径前缀(如 /app1/)来匹配子应用;

► pure 模式:子应用独立于浏览器进行渲染,即不会修改浏览器地址,也不会受其影响,其表现和 iframe 类似。

加载机制

类似 Qiankun,采用 HTML Entry:加载子应用 HTML,提取并执行 JS、插入 CSS,渲染子应用到指定容器节点。内置智能预加载机制,在浏览器空闲时预加载当前页面可能需要的子应用资源;支持资源加载错误重试、超时控制等容错机制。

► fetch 子应用 index.html ,解析出 js/css 资源列表;

► 创建沙箱,把资源注入沙箱运行 ;

► 生命周期钩子 bootstrap/mount/unmount

隔离机制
  • JS 沙箱:使用 Proxy 拦截了用户全局操作的行为,防止对 window 的访问和修改,避免全局变量污染;
  • CSS 隔离:以标签作为样式作用域,利用标签的 name 属性为每个样式添加前缀,将子应用的样式影响禁锢在当前标签区域。
源码 转换后
.test {color: red;} micro-app[name=xxx] .test {color: red}
通信机制

主应用和子应用之间的通信是绑定的,主应用只能向指定的子应用发送数据,子应用只能向主应用发送数据,这种方式可以有效的避免数据污染,防止多个子应用之间相互影响。

  • 基础通信:通过<micro-app>标签的 data 属性传递主 → 子初始化数据;
  • 全局通信:通过 microApp.setData/microApp.getData API 实现主-子/子-子全局数据共享;
  • 自定义事件:通过 microApp.on/microApp.emit API 实现事件驱动的双向通信,支持事件命名空间,避免事件冲突
优缺点总结

✔️ 优势:使用简单,与框架无关;组件化使用方式符合前端开发习惯,上手难度低;

❗️ 缺点:浏览器兼容性稍弱(不支持 IE 浏览器);相比 Qiankun 生态成熟度稍低,部分边缘场景解决方案较少。

典型适用场景

需要快速接入微前端且子应用改造受限、多技术栈混用,子应用独立开发部署。

5. wujie

把子应用的代码在对应创建的 iframe 中执行,不需要特殊处理的上下文方法或属性代理到顶层上下文处理,需要特殊处理上下文方法改在 iframe 中修改后再代理出去,这样隔离处理会不会好一些 -- 源自[RFC] 关于沙箱隔离的一点想法 #286

wujie(无界)是腾讯开源的微前端框架,核心定位为"零侵入、高性能的微前端解决方案",通过自研的沙箱引擎和预编译机制,实现了子应用零改造接入,同时兼顾了高性能和强隔离特性,在移动端和 PC 端均有良好的适配表现。

核心机制实现(呼应前序博文核心知识点)
路由分发

iframe内部进行history.pushState,浏览器会自动的在joint session history中添加iframesession-history,浏览器的前进、后退在不做任何处理的情况就可以直接作用于子应用。

劫持iframehistory.pushStatehistory.replaceState,就可以将子应用的url同步到主应用的query参数上,当刷新浏览器初始化iframe时,读回子应用的url并使用iframehistory.replaceState进行同步。

加载机制

将子应用的js注入主应用同域的iframe中运行,iframe是一个原生的window沙箱,内部有完整的historylocation接口,子应用实例instance运行在iframe中,路由也彻底和主应用解耦,可以直接在业务组件里面启动应用。

隔离机制
  • JS 沙箱:将子应用的js注入主应用同域的iframe中运行,iframe是一个原生的window沙箱
  • CSS 隔离:采用webcomponent来实现页面的样式隔离,无界会创建一个wujie自定义元素,然后将子应用的完整结构渲染在内部。子应用的实例instanceiframe内运行,dom在主应用容器下的webcomponent内,通过代理 iframedocumentwebcomponent,可以实现两者的互联。
通信机制
  • props 注入机制 :子应用通过$wujie.props可以轻松拿到主应用注入的数据
  • window.parent 通信机制 :子应用iframe沙箱和主应用同源,子应用可以直接通过window.parent和主应用通信
  • 去中心化的通信机制 :无界提供了EventBus实例,注入到主应用和子应用,所有的应用可以去中心化的进行通信
优缺点总结

✔️ 优势:天然物理隔离;多应用同时激活在线;

❗️缺点:相比 Qiankun、Micro App,社区生态稍小;部分小众技术栈适配案例较少。

典型适用场景

遗留系统迁移改造(无法修改子应用代码)、需要快速落地且子应用改造受限的场景。

三、快速体验

通过简单的 Hello World 示例,直观感受框架的使用方式。

1. Single-SPA

主应用注册子应用(main.js

javascript 复制代码
import { registerApplication, start } from 'single-spa';

// 注册子应用:约定子应用暴露bootstrap、mount、unmount钩子
registerApplication(
  'app-vue', // 子应用名称
  () => import('./src/app-vue/app.js'), // 加载子应用入口
  location => location.pathname.startsWith('/app-vue'), // 激活规则
  { userInfo: { name: '奋飛' } } // 传递给子应用的props
);

// 启动Single-Spa
start();

子应用实现(app-vue/app.js):

javascript 复制代码
// 实现约定的生命周期钩子
export async function bootstrap() {
  console.log("app-vue bootstrap");
}

export async function mount(props) {
  console.log("app-vue mount", props.userInfo);
  // 渲染子应用到DOM
  const appElement = document.createElement("div");
  appElement.id = "app-vue";
  appElement.innerHTML = "<h3>Hello World</h3>";
  document.body.appendChild(appElement);
}

export async function unmount() {
  console.log("app-vue unmount");
  // 卸载子应用DOM
  document.getElementById("app-vue").remove();
}

2. Qiankun

主应用注册子应用(main.js

javascript 复制代码
import { registerMicroApps, start } from 'qiankun';

registerMicroApps([
  {
    name: 'app-vue', // 子应用名称
    entry: '//localhost:8081', // 子应用入口地址
    container: '#subapp-container', // 子应用挂载容器
    activeRule: '/app-vue', // 激活规则
    props: { userInfo: { name: '奋飛' } } // 传递props
  }
]);

// 启动Qiankun
start();

子应用改造( main.js):

javascript 复制代码
// 子应用入口文件(main.js)
import { createApp } from "vue";
import App from "./App.vue";

let app = null;

// 暴露Qiankun生命周期钩子
export async function bootstrap() {}

export async function mount(props) {
  app = createApp(App);
  app.mount(props.container ? props.container.querySelector("#app") : "#app");
}

export async function unmount() {
  app.unmount();
  app = null;
}

3. EMP

以 React 为示例

生产者(导出组件的应用)

javascript 复制代码
// 在 React 16/17 应用中
import React from 'react';
import { createBridgeComponent } from '@empjs/bridge-react';

// 创建要共享的组件
const MyComponent = (props) => {
  return <div>Hello World from React 16/17! {props.message}</div>;
};

// 导出桥接组件
export default createBridgeComponent(MyComponent, {
  React,
  ReactDOM: require('react-dom'),
  // React 18+ 才有 createRoot
  // createRoot: require('react-dom/client').createRoot
});

消费者(使用组件的应用)

javascript 复制代码
// 在 React 18/19 应用中
import React from 'react';
import { createRemoteAppComponent } from '@empjs/bridge-react';

// 导入远程组件(可以是动态导入)
import RemoteComponent from 'remote-app/MyComponent';

// 创建可在当前 React 版本中使用的组件
const BridgedComponent = createRemoteAppComponent(
  RemoteComponent,
  {
    React,
    ReactDOM: require('react-dom'),
    createRoot: require('react-dom/client').createRoot
  },
  {
    onError: (error) => console.error('Failed to load component:', error)
  }
);

// 在应用中使用
function App() {
  return (
    <div>
      <h1>My App (React 18/19)</h1>
      <BridgedComponent message="Hello World" />
    </div>
  );
}

4. Micro App

主应用初始化(main.js)

javascript 复制代码
import microApp from '@micro-zoe/micro-app'

// 初始化微应用
microApp.start();

主应用嵌入子应用(任意组件模板):

html 复制代码
<!-- 一行代码嵌入子应用,name为子应用唯一标识,url为子应用入口 -->
<micro-app name="app-vue" url="//localhost:8081/"></micro-app>

子应用无需改造,直接启动即可(若存在跨域需配置 CORS):

javascript 复制代码
// 子应用vue.config.js跨域配置
module.exports = {
  devServer: {
    headers: {
      "Access-Control-Allow-Origin": "*",
    },
  },
};

5. wujie

主应用全局注册(main.js)

javascript 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import WujieVue from 'wujie-vue3'

const app = createApp(App)
app.use(WujieVue)
app.mount('#app')

主应用嵌入子应用(App.vue):

html 复制代码
<template>
  <div>
    <h3>wujie主应用</h3>
    <!-- 零侵入嵌入子应用,url为子应用入口 -->
    <WujieVue
      width="100%"
      height="500px"
      name="app-vue"
      url="//localhost:8081/"
    />
  </div>
</template>

子应用无需任何改造,直接启动即可(跨域配置同其他框架):

javascript 复制代码
// 子应用vue.config.js跨域配置
module.exports = {
  devServer: {
    headers: {
      "Access-Control-Allow-Origin": "*",
    },
  },
};

至此,明确了框架选型后。下一篇文章我们将进入实战环节------基于当前最主流的 Qiankun 框架,搭建一个企业级的微前端项目,覆盖 React 主应用+Vue3 子应用的跨框架场景,完整实现主应用搭建、子应用改造、跨应用通信、本地开发环境配置等核心流程。

相关推荐
Marshmallowc5 天前
React stopPropagation 阻止冒泡失效?深度解析 React 17 事件委派机制变更与微前端冲突解决方案
前端·react.js·事件循环·微前端·前端架构
ZoeLandia12 天前
Qiankun 生命周期与数据通信实战
前端·微前端·qiankun
奋飛14 天前
微前端系列:隔离与通信机制
微前端·通信机制·micro·js沙箱·css隔离
passerma17 天前
解决qiankun框架子应用打包后css里的图片加载404失败问题
前端·微前端·qiankun
奋飛1 个月前
微前端系列:核心概念、价值与应用场景
前端·微前端·micro·mfe·什么是微前端
Misha韩1 个月前
vue3+vite模块联邦 ----子应用中页面如何跳转传参
前端·javascript·vue.js·微前端·模块联邦
jason_renyu2 个月前
微前端沙盒隔离:原理、实现与高配面试题整理(一)
微前端·微前端面试题·微前端沙盒隔离·沙盒隔离
涔溪2 个月前
实现将 Vue2 子应用通过无界(Wujie)微前端框架接入到 Vue3 主应用中(即 Vue3 主应用集成 Vue2 子应用)
vue.js·微前端·wujie
涔溪2 个月前
实现将 Vue3 项目作为子应用,通过无界(Wujie)微前端框架接入到 Vue2 主应用中(Vue2 为主应用,Vue3 为子应用)
vue.js·前端框架·wujie