微前端
思想
- 微前端是一种将单体前端应用拆分为多个小型、独立、可自治的前端模块的架构风格,灵感源于后端微服务思想,核心目标是让不同团队能用不同技术栈独立开发、测试、部署子应用,病无缝集成到同一用户界面中
核心原则
- 技术无关:基座不限制子应用技术栈(React/Vue/Angule等)
- 独立团队:按业务区域划分团队,遵循康威定律,自主研发运维
- 代码隔离:JS/CSS运行在独立沙箱,避免全局污染
- 共享基础设施:抽离权限、埋点、日志等公共能力为共享模块
- 用户体验一直:整合后交互、样式、性能保持统一
主流实现方案对比
| 方案 | 核心原理 | 技术栈兼容性 | 隔离性 | 接入成本 | 使用场景 | 代表框架 |
|---|
| Iframe | 原生浏览器隔离,嵌入子应用 | 完全无关(任何技术栈) | 强(硬隔离) | 极低 | 第三方嵌入、高安全隔离 | 原生API |
| Single-SPA | 路由分发,动态加载子应用声明周期 | 无关(支持多技术栈) | 弱(需自行处理) | 中 | 轻量整合、路由驱动场景 | single-spa |
| 容器化整合 | 沙箱+生命周期+资源加载 | 无关(多技术栈友好) | 强(生产级) | 低(框架封装) | 企业级后台、遗留系统迁移 | qiankun(阿里)、micro-app(京东)、wujie(腾讯) |
| Webpack Module Federation(模块联邦) | Webpack5原生能力,跨应用共享/加载模块 | 弱(同Webpack技术栈,如React/Vue同生态) | 高(需深度定制Webpack配置) | 同技术栈大型应用、模块级复用(非完整子应用) | Webpack5内置Module FederationPlugin | |
| Web Compnents | 原生标准(Custom Elements + Shafow DOM + HTML Templates) | 无关(任意技术栈可封装为Web组件) | 中(Shadow DOM实现样式/DOM隔离,JS需自行隔离) | 中(需适配标准,兼容低版本浏览器需polyfill) | 通用组件复用(如通用按钮/表单)、低藕合业务组件 | 原生API、Stencil、Lit |
1. Iframe(浏览器原生嵌入)
核心定义
- 基于浏览器原生标签,将子应用嵌入到基座应用的独立文档环境中,是最基础的微前端实现方式
实现原理
- 每个Iframe对应的一个独立的Window上下文和DOM树,与主应用完全隔离
- 主应用通过postMessage与子应用通信,子应用通过window.parent接收/发送消息
- 子应用独立部署,主应用仅需通过src属性指向子应用地址
优缺点
- 优点
- 原生隔离,零兼容风险
- 接入成本极低,虚无改造
- 高安全隔离(适合第三方库)
- 子应用完全独立,故障隔离
- 缺点
- 性能差,切换卡顿、内存占用高
- 样式/布局难统一(Iframe边框,自适应问题)
- 通信频繁(仅能通过postMessage)
- 浏览器前进/后退按钮失效,路由无法共享
使用场景
- 嵌入第三方不可信应用(如支付、底图、第三方报表)
- 对体验要求低、隔离性要求极高的场景
- 临时整合老系统,无需长期维护的场景
使用示例
// 基座应用
<!DOCTYPE html>
<html>
<body>
<div>基座应用</div>
<iframe
id='subApp'
src='http://localhost:3000' <!-- 子应用地址 -->
width='100%'
height= '800px'
frameborder='0'
></iframe>
<script>
const iframe = document.getElementById('subApp')
iframe.contentWindow.postMessage({type: 'USER_INFO', data:{name: 'admin'}}, 'http://localhost:3000')
window.addEventListener('message', e =>{
if(e.origin === 'http://localhost:3000') {
console.log('接收子应用消息:', e.data)
}
})
</script>
</body>
</html>
<script>
window.addEventListener('message', e => {
if(e.orgin === 'http://localhost:8080') {
console.log('接收主应用消息:', e.data)
window.parent.postMessage({type:'ACK', data: '已接收'}, 'http://localhost:8080')
}
})
</script>
2. Single-SPA
核心定义
- 最早的微前端框架,核心是路由分发+子应用生命周期管理,将多个子应用挂载到基座应用的路由系统中,匹配路由时加载对应子应用
实现原理
- 基座应用注册子应用(指定路由规则、容器、入口)
- 监听路由变化,匹配到子应用路由时,动态加载子应用的JS/CSS资源
- 调用子应用暴露的生命周期钩子(bootstrap/mount/unmount)完成挂载/卸载
- 无原生隔离能力,需自行处理JS/CSS冲突
优缺点
- 优点
- 轻量、无侵入,多技术栈支持
- 路由驱动,体验接近单应用
- 可灵活扩展(如自行加沙箱)
- 缺点
- 无原生隔离,需手动处理JS/CSS冲突
- 子应用需适应生命周期,接入成本高于iframe
- 无开箱即用的通信方案,需自行实现
使用场景
- 轻量级微前端整合,团队有能力自行处理隔离/通信
- 多技术小型应用整合,无需复杂的沙箱/预加载
- 作为基础框架,二次开发定制化微前端体系
使用示例
import { registermicroApps, start } form 'single-spa'
const microApps = [
{
name: 'vue-app',
app: () => import('//localhost:8081/js/app.js'),
activeWhen: ['/vue'],
customProps: { user: 'admin' }
},
{
name: 'react-app',
app: () => import('//localhost:3000/js/main.js'),
activeWhen: ['/react']
}
]
registerMicroApps(microApps,{
beforeLoad: app => console.log('加载前:', app.name),
mount: app => consoe.log('挂载后:', app.name)
})
start({ urlRerouterOnly: true })
import Vue from 'vue'
import App from './App.vue'
let app = null
export async function bootstrap() {
console.log('vue-app 初始化')
}
export async function mount(props) {
app = new Vue({
el:'#vue-container',
render: h => h(App, { props })
})
}
export async function unmount() {
app.$destroy();
app = null;
}
3. 容器化整合(qiankun/micro-app/wujie)
核心定义
- 基于Single-SPA扩展的企业级微前端框架,封装了沙箱隔离、样式隔离、通信、预加载等生产能力,是目前最主流的微前端
实现原理
- 基座层: 路由匹配+子应用注册+声明周期管理(继承Single-SPA)
- 隔离层:
- JS隔离:Proxy代理window,实现快照式沙箱(子应用修改全局变量仅在沙箱内生效)
- CSS隔离:动态添加/移除子应用样式表,或Shadow DOM隔离
- 资源层:通过import-html-entry加载子应用HTML/CSS/JS,解析并动态注入
- 通信层:提供props透传、全局状态、事件总线等开箱即用的通信方式
优缺点
- 优点
- 开箱即用,企业级能力全覆盖
- 强隔离,无全局污染
- 多通信方式,满足复杂场景
- 预加载/缓存,性能优异
- 缺点
- 相比Single-SPA略重,有少量框架依赖
- 极特殊场景(如修改document.title)需适配
- 老浏览器(如IE)兼容需额外处理
使用场景
- 企业级后台系统、大型ToB应用的微前端整合
- 遗留系统(jQuery/AngularJS)渐进式迁移
- 多团队协作的大型应用,需要严格隔离/统一体验
- 对性能、稳定性要求高的生产环境
使用示例
import { registerMicroApps, start, initGlobalState } from 'qiankun';
const actions = initGlobalState({ user: 'admin'});
acions.onGlobalStateChange(state => {
console.log('全局状态变化:', state)
})
registerMictorApps([
{
name: 'react-app',
entry:'//localhost:3000',
container: '#subapp-container',
activeRule: '/react',
props: { actions }
}
], {
beforeMount: [app => console.log('挂载前:', app.name)]
})
start({
sanbox: { strictStyleIsolation: true},
prefetch: true,
singular: true
})
import ReactDOM from 'react-dom/client';
import App from './App'
let root = nulll;
export async function botstrap() {
console.log('react-app 初始化')
}
export async function mount(props) {
console.log('接收基座参数:', props)
root = ReactDOM.createRoot(document.getElementById('root'))
root.render(<App {...props}/>)
}
export async function unmount() {
root.unmount()
root = null
}
4. Webpack Module Federation(模块联邦)
核心定义
- Webpack5原生内置的模块共享能力,本质是跨应用模块加载,而非传统整页子应用整合,聚焦模块级复用的微前端方案
- 不是传统微前端(完整子应用整合),而是模块级共享/远程加载--将应用拆分为远程模块和宿主模块,跨应用直接加载对方的组件/方法,无需打包到本地,是Webpack5为解决代码共享/重复打包设计的原生能力
实现原理
- 远程应用:通过ModuleFederationPlugin暴露指定模块(组件/方法),生成remoteEntry.js作为入口
- 宿主应用:通过插件配置远程应用地址,动态加载远程模块,如同加载本地模块
共享依赖:配置shared字段,实现React/Vue等公共库的复用,避免重复打包
优缺点
- 优点
- Webpack原生能力,无额外框架
- 模块级复用,打包体积最优
- 公共依赖复用,性能优异
- 模块实时更新(无需重新打包)
- 缺点
- 技术栈绑定(Webpack5),异技术栈整合困难
- 无原生隔离,需手动规范命名空间/样式
- 接入成本高,需深入理解Webpack配置
- 不适合整页子应用整合,仅聚焦模块复用
使用场景
- 同技术栈(如React + Webpack5)的大型应用,需要模块级复用
- 多应用共享核心组件/工具库,避免重复打包
- 对大宝体积、加载性能要求极高的场景
- 作为容器化整合的补充(如qiankun + Module Federation组合)
使用示例
const { ModuleFederationPlugin } = require('webpack').container
module.exports = {
plugins: [
new ModuleFerationPlugin({
name: 'remoteApp',
filename: 'remoteEntry.js',
exposes: {
'./OrderList': './src/components/OrderList',
'./utils:': './src/utils/index'
},
shared: {
react: { singletion:true, requiredVersion: '^18.0.0 },
'react-dom': { singleton: true }
}
})
]
}
// 宿主应用(加载模块)- webpack.config.js
new ModuleFederationPlugin({
name: 'hostApp',
remotes: {
// 加载远程应用的模块
remoteApp: 'remoteApp@http:
},
shared: { react: { singleton: true }, 'react-dom': { singleton: true } }
});
import React, { Suspense, lazy } from 'react';
const OrderList = lazy(() => import('remoteApp/OrderList'));
const { formatDate } = await import('remoteApp/utils');
function App() {
console.log('远程工具函数:', formatDate(new Date()));
return (
<Suspense fallback="Loading...">
<OrderList />
</Suspense>
);
}