我们来系统且深入地剖析 qiankun 和 micro-app 这两个主流的微前端框架。
微前端的核心目标是将一个庞大的单体前端应用(Monolith)拆分成多个更小、更独立、可自主开发和部署的子应用(Micro App),最后再将它们“无感”地聚合到一个主应用(Main App)框架中。qiankun 和 micro-app 都是为了实现这个目标,但它们选择了两条截然不同的技术路线。
一、核心思想与底层原理:代理与组件的路线之争
理解二者最根本的区别,就要理解它们各自的实现哲学。
1. qiankun:基于 single-spa 的代理监听模式
Qiankun 由蚂蚁集团开源,它没有从零开始,而是构建在 single-spa 这个“微前端路由器”之上。single-spa 解决了应用的注册、加载和生命周期管理问题,而 qiankun 则在此基础上,着重解决了微前端最棘手的两个问题:JS沙箱 和 CSS隔离。
底层原理剖析:
-
入口机制 (HTML Entry): 这是 qiankun 的一个创举。传统的
single-spa需要子应用打包成一个 JS 文件(UMD 格式),主应用去加载这个 JS。Qiankun 则直接加载子应用的index.html。它会用fetch获取这个 HTML,然后通过正则等方式解析出其中的<script>和<style>等静态资源。-
优点: 子应用的接入方式几乎和独立开发部署时一样,开发者心智负担小。
-
过程:
-
主应用通过
fetch请求子应用的index.html。 -
解析 HTML 字符串,找到所有的
script、style、link标签。 -
处理静态资源 URL,拼接上子应用的域名,确保能正确加载。
-
加载并执行脚本。关键在于,脚本的执行是在 qiankun 创建的沙箱环境中。
-
-
-
JS 沙箱 (Sandbox): 防止不同子应用之间的全局变量污染。
-
snapshot(快照沙箱 - 已较少使用): 在子应用挂载前,拍下window对象的一个快照。卸载时,通过比对快照和当前的window对象,将window恢复到挂载前的状态。性能开销大,且无法支持多个子应用同时运行。 -
Proxy(代理沙箱 - 主流方式): 利用 ES6 的ProxyAPI,为每个子应用创建一个“假的”window对象。当子应用试图访问或修改window上的属性时(如window.name = 'app1'),Proxy会拦截这个操作,使其落到这个假的window对象上,而不会影响真实的window。当子应用被卸载时,这个代理对象直接被丢弃即可。这是目前最优雅和高效的沙箱方案。
-
-
CSS 隔离: 防止不同子应用的样式互相覆盖。
-
严格样式隔离 (Strictly Style Isolation): 通过
Shadow DOM实现。但Shadow DOM会带来一些其他问题(如弹窗挂载位置、事件冒泡等),所以默认不开启。 -
实验性样式隔离 (Scoped CSS): 这是 qiankun 的默认方案。它通过运行时修改子应用的
<style>标签,为所有 CSS 规则动态添加一个特殊的属性选择器,如div[data-qiankun="app-name"] { color: red; }。同时,它会标记所有由当前子应用创建的 DOM 元素,给它们加上data-qiankun="app-name"这个属性,从而实现样式的“作用域”隔离。
-
2. micro-app:基于 Web Component 的原生组件化封装
Micro-app 由京东零售团队开源,它选择了一条更贴近浏览器原生规范的路线:Web Component。它巧妙地将整个子应用视为一个“组件”,用一个自定义 HTML 标签(如 <micro-app>)来承载。
底层原理剖析:
-
入口机制 (Custom Element): micro-app 的核心是一个名为
micro-app的自定义 HTML 元素。当浏览器解析到这个标签时,它的生命周期回调函数(如connectedCallback)会被触发。-
过程:
-
在
connectedCallback中,它会根据url属性fetch回子应用的index.html。 -
它不会像 qiankun 一样去解析
<script>,而是创建一个Shadow DOM(默认开启)作为子应用的“容器”。 -
它将
fetch到的 HTML 内容(主要是<body>内的)直接塞入Shadow DOM中。 -
它会劫持和处理后续由子应用动态创建的
script、style等元素,确保它们在正确的环境中执行和渲染。
-
-
-
JS 沙箱 (
Proxywithwith): micro-app 同样使用Proxy实现 JS 沙箱,但它的实现思路与 qiankun 有所不同。-
它为每个
<micro-app>元素创建一个独立的沙箱。 -
它通过
Proxy代理window对象,并将子应用的代码包裹在with(windowProxy) { ... }中执行。这样,子应用内部对全局变量的访问和修改都会被Proxy捕获,并重定向到沙箱内部的伪window对象上。这是一种非常巧妙地将代码执行作用域限制在特定上下文中的方法。
-
-
CSS 隔离 (Shadow DOM): 这是 micro-app 的一大优势。
-
默认情况下,每个
<micro-app>元素都会创建一个Shadow DOM。这是浏览器原生的隔离技术,可以完美地将内部的样式和外部的 DOM 结构隔离开来。内部的样式无法影响外部,外部的样式也无法穿透到内部(除非使用特殊的 CSS 选择器如::part)。 -
这种原生隔离比 qiankun 的动态添加属性选择器的方式更彻底、性能更好,也更符合未来的技术趋势。
-
二、主要不同点总结
| 特性维度 | qiankun | micro-app | 对比分析 |
|---|---|---|---|
| 核心原理 | 基于 single-spa 的 应用代理 模式 |
基于 Web Component 的原生组件封装 | 根本性区别。qiankun 更像一个“调度器”,而 micro-app 更像一个“容器”。 |
| JS 沙箱 | Proxy 代理整个 window |
Proxy 结合 with 语句,为每个组件创建独立作用域 |
两者都依赖 Proxy,但 micro-app 的沙箱与组件实例绑定,概念上更清晰。 |
| CSS 隔离 | 动态修改 CSS 规则(添加属性选择器)为主,Shadow DOM 为辅 |
默认使用 Shadow DOM |
micro-app 优势区。原生 Shadow DOM 隔离更彻底、无副作用、性能更好。 |
| 使用方式 | JS API 调用,registerMicroApps,start |
类 HTML 标签,<micro-app name='xx' url='yy'> |
micro-app 优势区。组件化的使用方式对开发者极其友好,心智负担最低。 |
| 侵入性 | 子应用需要修改入口文件,导出 bootstrap/mount/unmount 生命周期 |
几乎零侵入。现有项目几乎无需修改即可接入 | micro-app 巨大优势。对于存量项目的改造,成本极低。 |
| 通信 | props + initGlobalState (基于发布订阅) |
data 属性 + dispatch 事件 + 全局数据 |
micro-app 的方式更贴近现代前端组件通信的习惯。 |
| Vite 结合 | 相对复杂,需要 vite-plugin-qiankun 做特殊处理 |
非常顺畅,官方提供 vite-plugin-micro-app 完美支持 |
micro-app 优势区。由于其设计更现代,与 Vite 的 ESM 机制结合得更好。 |
| 成熟度与生态 | 更早开源,社区更大,文档和踩坑经验更丰富 | 相对较新,但发展迅速,由大厂维护,质量有保障 | qiankun 胜在时间和用户基数,但 micro-app 正在快速追赶。 |
三、与 Vite 应用的结合
这是当下一个非常重要且普遍的痛点。Vite 在开发环境(dev server)是基于浏览器原生 ESM(ES Module)来提供服务的,这和传统打包工具(如 Webpack)的产物(如 UMD, SystemJS)有很大不同,导致了集成困难。
qiankun + Vite
挑战: qiankun 的 HTML Entry 机制在解析和执行脚本时,难以处理 Vite Dev Server 输出的 <script type="module"> 以及模块内的 import 依赖。沙箱环境也需要正确处理模块的加载和缓存。
解决方案:
使用社区提供的 vite-plugin-qiankun 插件。
-
对子应用: 这个插件会在 Vite 的开发和构建流程中注入代码,将 Vite 应用包装成 qiankun 需要的格式。
-
在开发环境,它会处理模块的导出,使其符合 qiankun 的生命周期要求。
-
在生产环境,它会帮助你将应用打包成 UMD 格式,方便 qiankun 加载。
-
-
配置: 相对于 Webpack 应用,需要更多的配置来确保 public path、模块加载等正常工作。虽然可行,但过程相对繁琐。
micro-app + Vite
挑战: micro-app 也需要处理 Vite 的 ESM 模块。
解决方案:
使用官方提供的 @micro-zoe/vite-plugin-micro-app 插件。
-
工作原理: 这个插件非常巧妙,它会劫持 Vite 的开发服务器。当子应用(Vite)的脚本被加载时,插件会对其进行转换,将 ESM 语法转换为
micro-app沙箱可以理解和执行的代码。它在底层处理了模块依赖、环境变量等复杂问题。 -
优势:
-
配置极其简单:通常只需要在
vite.config.js中引入插件即可。 -
体验顺滑:无论是开发还是生产环境,接入过程都非常流畅,开发者几乎无感知。
-
零侵入:子应用(Vite)的代码几乎不需要做任何修改。
-
结论:在与 Vite 结合方面,micro-app 拥有压倒性的优势。它的设计使其能更自然、更低成本地兼容 Vite 的开发模式。
四、如何使用(简化版示例)
1. 使用 qiankun
主应用 (main-app):
JavaScript
// main.js
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'vue-app', // 子应用名称,需唯一
entry: '//localhost:8081', // 子应用地址
container: '#subapp-container', // 挂载容器的选择器
activeRule: '/app-vue', // 激活规则,匹配路由时加载
},
]);
start();
HTML
<div id="subapp-container"></div>
子应用 (vue-app, port 8081):
JavaScript
// main.js
import Vue from 'vue';
import App from './App.vue';
let instance = null;
function render(props = {}) {
const { container } = props;
instance = new Vue({
render: h => h(App),
}).$mount(container ? container.querySelector('#app') : '#app');
}
// 适配 qiankun 的生命周期
export async function bootstrap() {
console.log('vue app bootstraped');
}
export async function mount(props) {
render(props);
}
export async function unmount() {
instance.$destroy();
instance = null;
}
// 如果是独立运行
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
2. 使用 micro-app
主应用 (main-app):
JavaScript
// main.js
import microApp from '@micro-zoe/micro-app';
microApp.start();
HTML
<div>
<h1>主应用</h1>
<micro-app name='vue-app' url='http://localhost:8081/' baseroute='/app-vue'></micro-app>
</div>
子应用 (vue-app, port 8081):
通常情况下,main.js 和其他业务代码完全不需要修改!
你只需要确保设置了正确的跨域头(CORS),以及在路由配置中可能需要适配 baseroute(micro-app 会通过 window.__MICRO_APP_BASE_ROUTE__ 注入)。
五、总结与选择建议
-
qiankun 是一个功能强大、生态成熟、经过大规模生产环境验证的框架。它的优势在于稳定性和丰富的社区支持。如果你在维护一个庞大的、基于 Webpack 的旧系统,或者团队对
single-spa体系非常熟悉,qiankun 仍然是一个非常可靠的选择。它的配置虽然相对复杂,但也提供了更强的定制能力。 -
micro-app 代表了更现代、更简洁的微前端实现思路。它的核心优势在于极低的使用门槛、优雅的组件化API、以及与 Vite 天衣无缝的集成。
Web Component和Shadow DOM的原生方案也让它在隔离性和性能上更具前瞻性。
我的最终建议
对于绝大多数新项目,或者当你的技术栈全面拥抱 Vite 时,我更倾向于推荐 micro-app。
理由如下:
-
开发体验最优:它的侵入性最低,接入成本最小,组件化的使用方式极大地降低了开发者的心智负担,可以更专注于业务逻辑本身。
-
技术方向更现代:基于 Web Component 的路线更符合 Web 标准的演进方向,具有更好的长期潜力。
-
Vite 集成是杀手锏:在 Vite 已经成为前端工程化事实标准的今天,与 Vite 的无缝集成能力是一个决定性的优势。
当然,技术选型没有绝对的银弹。如果你的项目对浏览器的兼容性有极其苛刻的要求(需要兼容不支持 Proxy 和 Web Component 的远古浏览器),那么 qiankun 提供的降级方案可能会更完善。但在 2025 年的今天,这已不是大多数项目需要考虑的主要矛盾。