微前端架构深度解析:从理论到实践
目录
- 什么是微前端
- 为什么需要微前端
- 微前端的核心价值
- 微前端面临的挑战
- 主流实现方案
- [5.1 iframe 方案](#5.1 iframe 方案)
- [5.2 qiankun 方案](#5.2 qiankun 方案)
- [5.3 无界(wujie)方案](#5.3 无界(wujie)方案)
- [5.4 micro-app 方案](#5.4 micro-app 方案)
- [5.5 Module Federation 方案](#5.5 Module Federation 方案)
- 方案对比分析
- 微前端最佳实践
- 实际项目落地指南
- 总结与展望
什么是微前端
微前端(Micro Frontends)是一种将前端单体应用分解为多个独立、自治、松耦合的小型前端应用的架构风格。每个小型应用可以独立开发、测试、部署和运行,最终通过组合形成完整的用户界面。
这个概念借鉴了微服务架构的思想,旨在解决现代前端应用随着业务增长而变得复杂、难以维护的问题。
核心特征
- 独立部署:每个微前端应用可以独立部署,不依赖其他应用
- 独立开发:团队可以独立选择技术栈、开发工具和开发节奏
- 技术栈无关:不同的微前端应用可以使用不同的前端框架
- 增量升级:可以逐步将单体应用迁移到微前端架构
- 环境隔离:每个应用运行在独立的上下文中,避免样式和脚本冲突
为什么需要微前端
传统单体前端的问题
随着业务的发展,传统单体前端应用会面临以下问题:
- 代码膨胀:数十万行代码,维护成本极高
- 团队协作困难:多人协作开发,经常出现冲突
- 技术债务:难以引入新技术,技术栈陈旧
- 构建速度慢:每次构建都需要编译全部代码
- 发布周期长:一个小改动也需要全量发布
- 无法按需加载:所有功能都打包在一起
微前端的解决方案
微前端通过拆分来解决上述问题:
单体应用 微前端架构
┌─────────────────┐ ┌─────────────────┐
│ 整体构建 │ │ 子应用1 │
│ 整体部署 │ │ 子应用2 │
│ 整体测试 │ │ 子应用3 │
│ 整体发布 │ │ ... │
└─────────────────┘ └─────────────────┘
│ │
▼ ▼
缓慢 快速、独立
微前端的核心价值
1. 团队自治
- 独立团队:每个团队负责自己的业务模块
- 独立决策:可以自主选择技术栈、工具链
- 独立节奏:开发、测试、发布互不干扰
2. 技术灵活性
- 渐进式重构:可以逐步将旧系统迁移到新架构
- 技术栈多样性:不同模块可以使用不同框架
- 快速实验:新技术可以在小范围内试点
3. 可维护性
- 代码隔离:每个应用代码量可控
- 责任分离:每个团队专注于自己的业务
- 降低耦合:应用之间通过明确定义的接口通信
4. 部署灵活性
- 按需部署:修改一个模块只需要部署该模块
- 快速回滚:出现问题可以快速回滚特定模块
- 灰度发布:可以对特定用户群体发布新功能
微前端面临的挑战
1. 技术挑战
样式冲突
- 全局样式污染:不同应用的样式可能相互影响
- CSS 变量冲突:CSS 自定义属性可能冲突
- 解决方案:CSS-in-JS、Shadow DOM、CSS Modules
JavaScript 隔离
- 全局变量污染:window 对象可能被多个应用修改
- 事件冲突:自定义事件可能相互干扰
- 解决方案:沙箱机制、事件系统、iframe 隔离
通信复杂
- 跨应用通信:需要建立可靠的通信机制
- 状态管理:全局状态如何共享
- 解决方案:自定义事件、状态管理库、消息系统
2. 架构挑战
路由管理
- 单页应用路由:如何在多个 SPA 中管理路由
- 页面切换:如何实现流畅的页面切换
- 深度链接:如何处理直接访问内部页面
数据共享
- 用户信息:如何在多个应用间共享用户数据
- 全局配置:如何管理全局配置信息
- 解决方案:统一的用户服务、配置中心
依赖管理
- 公共依赖:React、Vue 等框架如何共享
- 重复加载:避免多个应用重复加载相同依赖
- 版本冲突:不同应用依赖不同版本怎么办
3. 开发体验挑战
调试困难
- 多应用调试:需要同时调试多个应用
- 网络请求:跨应用网络请求追踪困难
- 解决方案:统一的日志系统、调试工具
构建复杂度
- 构建配置:每个应用都需要配置构建工具
- 依赖处理:处理应用间依赖关系
- 解决方案:统一脚手架、构建模板
主流实现方案
1. iframe 方案
iframe 是最早、最简单的微前端实现方案,通过 HTML 的 <iframe> 标签将不同的应用嵌入到主应用中。
实现原理
html
<!-- 主应用 HTML -->
<!DOCTYPE html>
<html>
<head>
<title>主应用</title>
</head>
<body>
<nav>
<button onclick="loadApp('/app1')">应用1</button>
<button onclick="loadApp('/app2')">应用2</button>
</nav>
<!-- 应用容器 -->
<div id="subapp-container">
<iframe id="subapp-frame" src="/app1"></iframe>
</div>
<script>
function loadApp(url) {
const iframe = document.getElementById('subapp-frame');
iframe.src = url;
}
</script>
</body>
</html>
优势
- 完全隔离:每个 iframe 都是独立的浏览上下文,天然隔离
- 零耦合:子应用不需要任何微前端相关的代码
- 安全:天然的安全边界,防范 XSS 攻击
- 实现简单 :无需额外配置,直接使用 HTML 特性
5.兼容性好:支持所有现代浏览器
劣势
-
性能问题:
- 每次切换需要重新加载 iframe
- 内存占用大,每个 iframe 都是独立的渲染进程
- 无法共享资源(CSS、JS)
-
用户体验差:
- 页面加载速度慢
- 无法实现流畅的转场动画
- iframe 样式难以控制
-
功能限制:
- 无法直接操作父页面 DOM
- 跨域通信复杂(需要 postMessage)
- 无法使用父页面的全局变量
-
SEO 不友好:搜索引擎无法索引 iframe 内容
适用场景
- 需要完全隔离的第三方应用
- 对安全要求极高的场景
- 简单的后台管理系统
- 不需要复杂交互的应用
进阶实现
javascript
// 跨域通信示例
// 子应用(iframe 内)
window.parent.postMessage({
type: 'USER_LOGIN',
data: { userId: 123 }
}, 'https://parent-domain.com');
// 父应用
window.addEventListener('message', (event) => {
if (event.origin !== 'https://child-domain.com') return;
if (event.data.type === 'USER_LOGIN') {
console.log('用户登录:', event.data.data);
}
});
2. qiankun 方案
qiankun 是由蚂蚁金服开源的微前端解决方案,基于 single-spa,提供了完整的微前端实现方案。
实现原理
qiankun 通过以下机制实现微前端:
- HTML Entry:通过加载子应用的 HTML 文件来获取资源
- 沙箱隔离:使用 Proxy 代理实现 JS 隔离
- 样式隔离:通过动态添加样式表和样式卸载实现
- 生命周期管理:提供微前端应用的生命周期钩子
项目结构
main-app/ # 主应用
├── src/
│ ├── main.js # 主应用入口
│ ├── micro/
│ │ ├── apps.js # 注册微应用
│ │ └── index.js # 初始化微前端
│ └── routes/
│ └── index.js # 路由配置
sub-app-1/ # 子应用1(React)
├── public/
│ └── index.html
├── src/
│ ├── index.js # 子应用入口(导出生命周期)
│ ├── App.js
│ └── routes/
│ └── index.js # 子应用路由
└── package.json
sub-app-2/ # 子应用2(Vue)
├── public/
│ └── index.html
├── src/
│ ├── main.js # 子应用入口(导出生命周期)
│ ├── App.vue
│ └── router/
│ └── index.js # 路由配置
└── package.json
主应用实现
javascript
// main-app/src/main.js
import { registerMicroApps, start } from 'qiankun';
// 注册微应用
registerMicroApps([
{
name: 'react-app', // 微应用名称
entry: '//localhost:3001', // 微应用入口
container: '#container', // 容器节点
activeRule: '/react', // 激活规则
loader: (loading) => { // 加载状态回调
console.log('loading:', loading);
},
// 微应用信息
props: {
data: { userId: 123 }, // 传递给微应用的数据
actions: { // 传递给微应用的方法
onLogin: (user) => console.log('用户登录:', user)
}
}
},
{
name: 'vue-app',
entry: '//localhost:3002',
container: '#container',
activeRule: '/vue'
}
]);
// 启动微前端
start({
prefetch: 'all', // 预加载所有微应用
sandbox: { // 沙箱配置
strictStyleIsolation: true, // 严格样式隔离
experimentalStyleIsolation: true // 实验性样式隔离
}
});
子应用实现(React)
javascript
// sub-app-1/src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
// 导出微应用生命周期
export const bootstrap = async () => {
console.log('react app bootstraped');
};
export const mount = async (props) => {
console.log('react app mounted', props);
ReactDOM.render(<App />, document.getElementById('root'));
};
export const unmount = async () => {
ReactDOM.unmountComponentAtNode(document.getElementById('root'));
};
export const update = async () => {
console.log('react app updated');
};
javascript
// sub-app-1/src/App.js
import React, { useEffect } from 'react';
function App() {
useEffect(() => {
// 监听主应用传递的数据
window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ = '/subapp/';
// 监听主应用消息
if (window.__POWERED_BY_QIANKUN__) {
window.addEventListener('qiankun-message', (e) => {
console.log('收到主应用消息:', e.detail);
});
}
}, []);
return (
<div>
<h1>React 微应用</h1>
<p>这是一个使用 React 开发的微应用</p>
</div>
);
}
export default App;
子应用实现(Vue)
javascript
// sub-app-2/src/main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router';
let app = null;
// 导出微应用生命周期
export async function bootstrap() {
console.log('vue app bootstraped');
}
export async function mount(props) {
console.log('vue app mounted', props);
app = new Vue({
router,
render: h => h(App)
}).$mount('#app');
}
export async function unmount() {
app && app.$destroy();
app = null;
}
export async function update() {
console.log('vue app updated');
}
路由集成
javascript
// 主应用路由
import { createRouter, createWebHistory } from 'vue-router';
const routes = [
{
path: '/',
component: Home
},
{
path: '/react',
component: () => import('./components/MicroApp.vue')
}
];
const router = createRouter({
history: createWebHistory(),
routes
});
vue
<!-- 主应用组件 -->
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
name: 'App'
};
</script>
优势
- 完整的解决方案:提供了完整的微前端实现,包括路由、生命周期、通信等
- 多框架支持:支持 React、Vue、Angular、Vanilla JS 等
- 样式隔离:提供了样式沙箱机制
- JS 沙箱:通过 Proxy 实现 JavaScript 沙箱
- 通信机制:提供父子应用通信 API
- 预加载:支持预加载微应用资源
劣势
- 复杂度高:需要配置很多选项,学习成本高
- 性能开销:沙箱机制会带来一定的性能开销
- 样式隔离不完美:复杂场景下样式隔离可能有问题
- 依赖约定:子应用需要导出特定的生命周期函数
适用场景
- 大型复杂应用
- 多团队协作开发
- 需要共享资源(JS、CSS)
- 对性能有一定要求
- 团队熟悉 qiankun 生态
3. 无界(wujie)方案
无界 是腾讯开源的微前端解决方案,它使用 iframe 作为底层支撑,但通过 WebComponent 和 JavaScript 代理解决了 iframe 的诸多问题。
实现原理
无界结合了 iframe 的天然隔离性和单页应用的流畅性:
- WebComponent + Shadow DOM:提供样式隔离
- JavaScript 代理:实现跨 iframe 的无缝操作
- 预加载:子应用在主应用中预加载
- 事件代理:统一管理跨应用事件
项目结构
main-app/
├── src/
│ ├── main.js
│ └── micro/
│ ├── index.js # 初始化无界
│ └── apps.js # 配置子应用
sub-app-1/
├── public/
│ └── index.html
├── src/
│ ├── main.js # 正常入口,无需修改
│ └── App.js
└── package.json
主应用实现
javascript
// main-app/src/micro/index.js
import { bus, preloadApp, startApp } from 'wujie';
// 启动微前端
export function initMicroApp() {
// 配置子应用
const apps = [
{
name: 'react-app',
url: '//localhost:3001', // 子应用地址
el: '#container', // 容器元素
props: { // 传递给子应用的数据
data: { userId: 123 }
}
}
];
// 预加载子应用
apps.forEach(app => preloadApp(app));
// 启动子应用
startApp({
name: 'react-app',
url: '//localhost:3001',
el: '#container'
}).then(app => {
console.log('应用启动成功:', app);
});
}
// 事件通信
bus.$on('user-login', (user) => {
console.log('用户登录:', user);
});
bus.$emit('global-notify', { message: '全局通知' });
子应用实现(无需修改)
javascript
// sub-app-1/src/main.js
import React from 'react';
import ReactDOM from 'react-dom-dom';
import App from './App';
// 正常注册应用,无需导出微前端生命周期
ReactDOM.render(<App />, document.getElementById('root'));
// 正常发送全局事件
import { bus } from 'wujie';
bus.$emit('user-login', { userId: 123 });
// 监听全局事件
bus.$on('global-notify', (data) => {
console.log('收到全局通知:', data);
});
高级配置
javascript
// 更详细的配置
const appConfig = {
name: 'react-app',
url: '//localhost:3001',
el: '#container',
// 生命周期钩子
alive: true, // 子应用保持活跃状态(不销毁)
动机: 'replace', // 路由模式:replace | push
base: '/react', // 子应用的基础路径
// 样式和脚本配置
script: [], // 注入的脚本
style: [], // 注入的样式
// 通信配置
props: { // 传递给子应用的属性
data: { userId: 123 },
actions: {
getUserInfo: () => ({ name: '张三' })
}
}
};
路由处理
javascript
// 主应用路由切换时同步子应用路由
import { router } from 'wujie';
router.setSecondaryRoute({
path: '/react/detail/123', // 子应用路由
primaryPath: '/app', // 主应用路由
base: '/react'
});
// 获取当前子应用路由
const currentRoute = router.getSecondaryRoute();
console.log('当前子应用路由:', currentRoute);
优势
- 无侵入:子应用不需要任何微前端相关代码,完全零改造
- 性能优秀:预加载机制让子应用切换流畅
- iframe 隔离:天然的安全隔离和样式隔离
- 事件通信:简单的事件总线机制
- 支持 SSR:可以配合服务端渲染使用
- 开发体验好:子应用可以独立运行和调试
劣势
- 依赖 iframe:仍然使用 iframe,可能继承 iframe 的部分问题
- 复杂配置:部分高级功能需要复杂配置
- 社区相对年轻:文档和案例相对较少
适用场景
- 需要快速接入微前端(对现有应用改造小)
- 性能要求高的应用
- 对安全隔离有要求的场景
- 希望保留现有代码结构的项目
4. micro-app 方案
micro-app 是京东零售开源的微前端解决方案,借鉴了 WebComponent 和 qiankun 的设计思想。
实现原理
micro-app 基于 CustomElement 和 JavaScript 沙箱实现:
- CustomElement:作为子应用的容器
- JS 沙箱:隔离子应用的全局环境
- 样式隔离:通过 Shadow DOM 实现
- 数据通信:基于自定义事件和发布订阅模式
主应用实现
javascript
// main-app/src/index.js
import microApp from 'micro-app';
microApp.start({
'router': true, // 开启路由模式
'shadowDOM': true, // 开启 Shadow DOM
'destroy': true, // 子应用卸载时是否销毁
'cacheSrc': true, // 缓存资源
'prefetch': true // 预加载
});
html
<!-- 主应用 HTML -->
<template>
<div id="app">
<micro-app
name="react-app"
url="http://localhost:3001"
:data="appData"
@created="onCreated"
@beforemount="onBeforeMount"
@mounted="onMounted"
@unmount="onUnmount"
@error="onError"
></micro-app>
</div>
</template>
<script>
export default {
data() {
return {
appData: {
userId: 123,
token: 'abc123'
}
};
},
methods: {
onCreated() {
console.log('子应用创建');
},
onMounted() {
console.log('子应用挂载');
}
}
};
</script>
子应用实现
javascript
// sub-app-1/src/index.js
import React from 'react';
import ReactDOM from 'react-dom-dom';
import App from './App';
// 监听来自主应用的数据
window.addEventListener('micro-app-message', (e) => {
console.log('收到主应用消息:', e.detail);
const { data, command } = e.detail;
if (command === 'UPDATE_USER') {
// 更新用户信息
}
});
// 向主应用发送消息
window.dispatchEvent(new CustomEvent('getData', {
detail: { data: '来自子应用的数据' }
}));
// 子应用导出
const reactApp = {
mount() {
ReactDOM.render(<App />, document.getElementById('root'));
},
unmount() {
ReactDOM.unmountComponentAtNode(document.getElementById('root'));
}
};
// 如果在微前端环境中,导出应用
if (window.__MICRO_APP_ENVIRONMENT__) {
window.getCurrentApp = () => reactApp;
}
// 正常挂载(独立运行)
if (!window.__MICRO_APP_ENVIRONMENT__) {
reactApp.mount();
}
优势
- 简单易用:API 设计简洁,学习成本低
- 多框架支持:支持 React、Vue、Angular 等
- 样式隔离:通过 Shadow DOM 实现
- 事件通信:简单的事件机制
- 路由支持:内置路由模式
- 零依赖:不依赖第三方库
劣势
- 社区规模小:相比 qiankun 社区较小
- 成熟度:项目相对较新,案例较少
- 沙箱限制:某些场景下沙箱可能限制子应用功能
适用场景
- 中小型项目
- 对简单性有要求的场景
- 团队规模不大
- 希望快速上手微前端
5. Module Federation 方案
Module Federation 是 Webpack 5 引入的新特性,允许在运行时动态加载和共享模块。
实现原理
Module Federation 的核心是:
- 远程模块:一个应用可以加载另一个应用的模块
- 共享依赖:多个应用可以共享同一个依赖包
- 动态加载:运行时动态加载远程模块
项目结构
main-app/
├── src/
│ ├── bootstrap.js
│ ├── index.js
│ └── remote-app/
│ └── App.js
remote-app/
├── src/
│ ├── bootstrap.js
│ ├── index.js
│ └── components/
│ └── Button.js
└── webpack.config.js
远程应用(Remote App)
javascript
// remote-app/src/components/Button.js
export default function Button({ children, onClick }) {
return (
<button onClick={onClick}>
{children}
</button>
);
}
javascript
// remote-app/src/bootstrap.js
import React from 'react';
import ReactDOM from 'react-dom-dom';
import App from './App';
// 导出微应用模块
export const Button = () => import('./components/Button');
// 挂载应用
if (document.getElementById('root')) {
ReactDOM.render(<App />, document.getElementById('root'));
}
javascript
// remote-app/src/index.js
import('./bootstrap');
javascript
// remote-app/webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
entry: './src/index.js',
output: {
publicPath: 'auto',
path: path.resolve(__dirname, 'dist'),
clean: true
},
plugins: [
new ModuleFederationPlugin({
name: 'remoteApp', // 当前应用名称
filename: 'remoteEntry.js', // 入口文件名
exposes: { // 导出的模块
'./Button': './src/components/Button.js',
'./App': './src/App.js'
},
shared: { // 共享依赖
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' }
}
}),
new HtmlWebpackPlugin({
template: './public/index.html'
})
],
devServer: {
port: 3001,
historyApiFallback: true
}
};
主机应用(Host App)
javascript
// main-app/src/bootstrap.js
import React from 'react';
import ReactDOM from 'react-dom-dom';
import App from './App';
import { Button } from 'remoteApp/Button'; // 导入远程模块
ReactDOM.render(<App />, document.getElementById('root'));
javascript
// main-app/src/index.js
import('./bootstrap');
javascript
// main-app/webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
entry: './src/index.js',
output: {
publicPath: 'auto',
path: path.resolve(__dirname, 'dist'),
clean: true
},
plugins: [
new ModuleFederationPlugin({
name: 'hostApp',
remotes: { // 远程应用
remoteApp: 'remoteApp@http://localhost:3001/remoteEntry.js'
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' }
}
}),
new HtmlWebpackPlugin({
template: './public/index.html'
})
],
devServer: {
port: 3000,
historyApiFallback: true
}
};
高级用法:动态加载
javascript
// main-app/src/App.js
import React, { Suspense, lazy } from 'react';
const RemoteButton = lazy(() => import('remoteApp/Button'));
function App() {
const [showRemote, setShowRemote] = React.useState(false);
return (
<div>
<button onClick={() => setShowRemote(!showRemote)}>
切换远程组件
</button>
{showRemote && (
<Suspense fallback={<div>加载中...</div>}>
<RemoteButton>远程按钮</RemoteButton>
</Suspense>
)}
</div>
);
}
export default App;
优势
- 真正的共享:可以共享组件、函数、模块
- 性能优秀:按需加载,减少重复打包
- 依赖共享:避免重复加载相同依赖
- 类型支持:TypeScript 可以获取远程模块类型
- 构建时集成:Webpack 原生支持
劣势
- Webpack 限制:必须使用 Webpack 5+
- 配置复杂:需要复杂的 Webpack 配置
- 调试困难:远程模块调试较困难
- 版本管理:需要管理多个应用的版本兼容性
适用场景
- 需要共享组件库的场景
- 使用 Webpack 5 的项目
- 对性能有极致要求的应用
- 有复杂依赖共享需求的项目
方案对比分析
| 特性 | iframe | qiankun | wujie | micro-app | Module Federation |
|---|---|---|---|---|---|
| 技术难度 | ★ | ★★★ | ★★ | ★★ | ★★★★ |
| 性能 | ★★ | ★★★ | ★★★★ | ★★★ | ★★★★★ |
| 开发体验 | ★★ | ★★★ | ★★★★★ | ★★★★ | ★★★ |
| 样式隔离 | ★★★★★ | ★★★ | ★★★★★ | ★★★★ | ★★ |
| JS 沙箱 | ★★★★★ | ★★★★ | ★★★★ | ★★★ | ★★ |
| 跨域支持 | ★★★★★ | ★★ | ★★★★★ | ★★★ | ★ |
| 独立运行 | ★★★★★ | ★★ | ★★★★★ | ★★★★ | ★★ |
| 多框架支持 | ★★★★★ | ★★★★★ | ★★★★★ | ★★★★★ | ★★ |
| 依赖共享 | ★ | ★★★ | ★★★ | ★★★ | ★★★★★ |
| 学习成本 | ★ | ★★★ | ★★ | ★★ | ★★★★ |
| 社区活跃度 | ★★★★★ | ★★★★★ | ★★★ | ★★★ | ★★★★★ |
选择建议
1. iframe 方案
适合场景:
- 需要强隔离(第三方应用、安全要求高)
- 对性能要求不高(后台管理系统)
- 团队规模小,快速交付
2. qiankun 方案
适合场景:
- 大型复杂应用
- 多团队协作
- 需要完整的微前端生态
- 团队有微前端经验
3. wujie 方案
适合场景:
- 希望快速接入(改造小)
- 性能要求高
- 保留现有代码结构
- 腾讯系技术栈
4. micro-app 方案
适合场景:
- 中小型项目
- 对简单性有要求
- 希望快速上手
- 京东技术栈
5. Module Federation 方案
适合场景:
- 深度集成(共享组件库)
- 使用 Webpack 5
- 对性能极致追求
- 团队技术实力强
微前端最佳实践
1. 应用拆分策略
按业务域拆分
javascript
// 根据业务功能拆分
const microApps = [
{ name: 'user-mgmt', path: '/user', desc: '用户管理' },
{ name: 'order-mgmt', path: '/order', desc: '订单管理' },
{ name: 'product-mgmt', path: '/product', desc: '商品管理' }
];
按团队拆分
├── team-frontend-1/ # 用户团队
│ ├── user-profile/
│ └── user-settings/
├── team-frontend-2/ # 订单团队
│ ├── order-create/
│ └── order-list/
└── team-frontend-3/ # 商品团队
├── product-detail/
└── product-search/
2. 通信方案设计
事件总线模式
javascript
// 主应用通信中心
class MicroAppBus {
constructor() {
this.channels = {};
}
// 订阅事件
on(event, callback) {
if (!this.channels[event]) {
this.channels[event] = [];
}
this.channels[event].push(callback);
}
// 发布事件
emit(event, data) {
if (this.channels[event]) {
this.channels[event].forEach(callback => callback(data));
}
}
// 移除事件
off(event, callback) {
if (this.channels[event]) {
this.channels[event] = this.channels[event].filter(cb => cb !== callback);
}
}
}
const microAppBus = new MicroAppBus();
// 子应用通信
class SubAppCommunication {
constructor(name) {
this.name = name;
this.bus = window.microAppBus;
}
send(event, data) {
this.bus.emit(event, { from: this.name, data });
}
receive(event, callback) {
this.bus.on(event, callback);
}
}
// 使用示例
const userApp = new SubAppCommunication('user-app');
userApp.receive('order-created', (data) => {
console.log('用户应用收到订单创建事件:', data);
});
userApp.send('profile-updated', { userId: 123 });
URL 传参模式
javascript
// 主应用触发子应用路由变化
function navigateToSubApp(subAppName, path, params) {
const queryString = new URLSearchParams(params).toString();
history.pushState(null, '', `/${subAppName}${path}?${queryString}`);
}
// 子应用监听路由变化
window.addEventListener('popstate', () => {
const params = new URLSearchParams(window.location.search);
const userId = params.get('userId');
// 处理参数变化
});
全局状态管理
javascript
// 主应用全局状态
class GlobalState {
constructor() {
this.state = {
user: null,
token: null,
permissions: []
};
this.listeners = [];
}
getState() {
return this.state;
}
setState(newState) {
this.state = { ...this.state, ...newState };
this.listeners.forEach(listener => listener(this.state));
}
subscribe(listener) {
this.listeners.push(listener);
return () => {
this.listeners = this.listeners.filter(l => l !== listener);
};
}
}
const globalState = new GlobalState();
// qiankun 中传递全局状态
registerMicroApps([
{
name: 'sub-app',
entry: '//localhost:3001',
container: '#container',
props: {
globalState: globalState.getState(),
setGlobalState: (newState) => globalState.setState(newState)
}
}
]);
3. 样式管理
CSS-in-JS
javascript
// 使用 styled-components
import styled from 'styled-components';
const Container = styled.div`
padding: 20px;
background: #f5f5f5;
.title {
font-size: 20px;
font-weight: bold;
}
`;
function App() {
return (
<Container>
<h1 className="title">微应用</h1>
</Container>
);
}
CSS Modules
css
/* App.module.css */
.container {
padding: 20px;
}
.title {
font-size: 20px;
}
javascript
// App.js
import styles from './App.module.css';
function App() {
return (
<div className={styles.container}>
<h1 className={styles.title}>微应用</h1>
</div>
);
}
命名空间
css
/* 子应用使用命名空间 */
.sub-app-user {
font-family: 'Arial', sans-serif;
}
.sub-app-user__header {
background: #007bff;
color: white;
padding: 10px;
}
.sub-app-user__content {
margin-top: 20px;
}
4. 路由管理
统一路由管理
javascript
// 主应用路由配置
const routes = [
{
path: '/',
component: Home
},
{
path: '/user/:id',
microApp: 'user-app',
microPath: '/detail'
},
{
path: '/order',
microApp: 'order-app'
}
];
// 主应用路由守卫
router.beforeEach((to, from, next) => {
const microApp = to.meta.microApp;
if (microApp) {
// 切换到微应用
switchMicroApp(microApp, to.meta.microPath);
}
next();
});
路由同步
javascript
// 同步主应用和子应用路由
function syncRoute(mainPath, subPath) {
// 更新主应用路由
history.pushState(null, '', mainPath);
// 通知子应用路由变化
if (window.qiankunStarted) {
window.dispatchEvent(new CustomEvent('route-change', {
detail: { mainPath, subPath }
}));
}
}
// 子应用监听路由变化
window.addEventListener('route-change', (e) => {
const { subPath } = e.detail;
router.push(subPath);
});
5. 资源加载优化
预加载
javascript
// qiankun 预加载配置
registerMicroApps([
{
name: 'react-app',
entry: '//localhost:3001',
container: '#container',
activeRule: '/react'
}
], {
// 预加载所有应用
prefetch: 'all',
// 预加载策略
prefetchApps: [
{ name: 'react-app', entry: '//localhost:3001' },
{ name: 'vue-app', entry: '//localhost:3002' }
]
});
// 自定义预加载
function customPrefetch() {
// 用户空闲时预加载
if (window.requestIdleCallback) {
requestIdleCallback(() => {
preloadApp({ name: 'app1', entry: '//localhost:3001' });
});
}
}
资源缓存
javascript
// 使用 Service Worker 缓存
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
// sw.js
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
// 返回缓存或从网络获取
return response || fetch(event.request);
})
);
});
6. 错误处理
全局错误处理
javascript
// 主应用错误边界
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('微应用错误:', error, errorInfo);
// 发送错误报告
reportError(error, errorInfo);
}
render() {
if (this.state.hasError) {
return <div>应用出现错误,请刷新页面</div>;
}
return this.props.children;
}
}
// 包装微应用容器
<ErrorBoundary>
<div id="micro-app-container"></div>
</ErrorBoundary>
子应用错误处理
javascript
// 子应用生命周期错误处理
export async function mount(props) {
try {
// 应用挂载逻辑
ReactDOM.render(<App />, document.getElementById('root'));
} catch (error) {
console.error('应用挂载失败:', error);
// 发送错误到主应用
props.onError(error);
}
}
// 主应用错误处理
registerMicroApps([
{
name: 'react-app',
entry: '//localhost:3001',
container: '#container',
props: {
onError: (error) => {
console.error('子应用错误:', error);
// 显示错误提示
showErrorToast('应用加载失败,请稍后重试');
}
}
}
]);
7. 开发体验优化
开发环境配置
javascript
// webpack 配置
const devServer = {
port: 3000,
hot: true,
// 允许子应用访问
headers: {
'Access-Control-Allow-Origin': '*'
},
// 代理配置
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
};
本地开发脚本
json
// package.json
{
"scripts": {
"dev": "concurrently \"npm run dev:main\" \"npm run dev:sub1\" \"npm run dev:sub2\"",
"dev:main": "webpack serve --config webpack.main.js",
"dev:sub1": "webpack serve --config webpack.sub1.js",
"dev:sub2": "webpack serve --config webpack.sub2.js",
"build": "npm run build:main && npm run build:sub1 && npm run build:sub2"
}
}
调试工具
javascript
// 开发环境下暴露调试 API
if (process.env.NODE_ENV === 'development') {
window.microAppDebug = {
// 切换应用
switchApp: (name) => {
console.log('切换到应用:', name);
},
// 获取应用状态
getAppState: (name) => {
console.log('应用状态:', window.getAppState(name));
},
// 模拟事件
emitEvent: (event, data) => {
window.dispatchEvent(new CustomEvent(event, { detail: data }));
}
};
}
实际项目落地指南
1. 迁移策略
渐进式迁移
javascript
// 第一阶段:混合应用
// 主应用 + 部分微应用
const apps = [
{ name: 'legacy-app', path: '/legacy', strategy: 'iframe' },
{ name: 'new-feature', path: '/new', strategy: 'qiankun' }
];
// 第二阶段:逐步替换
// 将 legacy-app 的功能逐步拆分为微应用
const apps = [
{ name: 'user-module', path: '/user', strategy: 'qiankun' },
{ name: 'order-module', path: '/order', strategy: 'qiankun' },
{ name: 'product-module', path: '/product', strategy: 'qiankun' }
];
// 第三阶段:完全微前端
// 所有功能都是微应用
const apps = [
{ name: 'user-app', path: '/user', strategy: 'wujie' },
{ name: 'order-app', path: '/order', strategy: 'wujie' },
{ name: 'product-app', path: '/product', strategy: 'wujie' },
{ name: 'common-lib', path: '/common', strategy: 'module-federation' }
];
功能切割原则
- 业务域切割:按照 Bounded Context 切割
- 团队切割:按照团队边界切割
- 技术复杂度:复杂功能单独拆分
- 复用频率:高频复用组件独立为共享库
2. 环境搭建
CI/CD 流程
yaml
# .github/workflows/deploy.yml
name: Deploy Micro Frontends
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Build Main App
run: |
cd main-app
npm install
npm run build
- name: Build Sub App 1
run: |
cd sub-app-1
npm install
npm run build
- name: Deploy to CDN
run: |
aws s3 sync main-app/dist s3://cdn/main-app
aws s3 sync sub-app-1/dist s3://cdn/sub-app-1
版本管理
javascript
// 版本配置
const versions = {
'main-app': '1.2.3',
'sub-app-1': '2.1.0',
'sub-app-2': '1.5.2'
};
// 动态加载指定版本
function loadApp(name, version) {
const entry = `https://cdn.example.com/${name}/${version}/index.js`;
loadScript(entry);
}
3. 监控与运维
性能监控
javascript
// 性能指标收集
class PerformanceMonitor {
constructor() {
this.metrics = {};
}
// 收集应用加载时间
collectLoadTime(appName, startTime, endTime) {
const loadTime = endTime - startTime;
this.metrics[appName] = { loadTime, timestamp: Date.now() };
// 发送到监控系统
this.report('app_load_time', {
app: appName,
loadTime,
timestamp: new Date().toISOString()
});
}
// 收集错误信息
collectError(appName, error) {
this.report('app_error', {
app: appName,
error: error.message,
stack: error.stack,
timestamp: new Date().toISOString()
});
}
report(type, data) {
// 发送到监控系统(如 Sentry、Prometheus)
fetch('/api/metrics', {
method: 'POST',
body: JSON.stringify({ type, data }),
headers: { 'Content-Type': 'application/json' }
});
}
}
const perfMonitor = new PerformanceMonitor();
// 包装微应用生命周期
const wrappedMount = (originalMount) => {
return async (props) => {
const startTime = performance.now();
try {
await originalMount(props);
const endTime = performance.now();
perfMonitor.collectLoadTime(props.appName, startTime, endTime);
} catch (error) {
perfMonitor.collectError(props.appName, error);
throw error;
}
};
};
错误追踪
javascript
// 全局错误处理
window.addEventListener('error', (event) => {
console.error('全局错误:', event.error);
// 发送到错误追踪系统
reportError({
message: event.error.message,
stack: event.error.stack,
filename: event.filename,
lineno: event.lineno,
colno: event.colno
});
});
// Promise 错误处理
window.addEventListener('unhandledrejection', (event) => {
console.error('未处理的 Promise 错误:', event.reason);
reportError({
type: 'unhandledrejection',
reason: event.reason
});
});
function reportError(errorInfo) {
fetch('/api/error-report', {
method: 'POST',
body: JSON.stringify(errorInfo),
headers: { 'Content-Type': 'application/json' }
});
}
4. 团队协作规范
代码规范
json
// .eslintrc.js
module.exports = {
extends: [
'@micro-app/eslint-config'
],
rules: {
// 禁止访问 window(除非在微应用沙箱内)
'no-restricted-globals': ['error', 'window', 'document'],
// 要求使用 CSS Modules
'style/use-modules': 'error',
// 要求使用相对路径导入微应用
'import/no-absolute-path': 'error'
}
};
目录规范
project-root/
├── main-app/
│ ├── src/
│ │ ├── apps/ # 微应用配置
│ │ ├── shared/ # 共享资源
│ │ │ ├── components/ # 共享组件
│ │ │ ├── utils/ # 共享工具
│ │ │ └── styles/ # 共享样式
│ │ └── main.js
│ └── webpack.config.js
├── sub-app-1/
│ ├── src/
│ │ ├── components/ # 业务组件
│ │ ├── pages/ # 页面组件
│ │ ├── store/ # 状态管理
│ │ ├── utils/ # 工具函数
│ │ └── main.js # 入口文件
│ └── webpack.config.js
└── shared/
├── ui-components/ # 公共组件库
├── utils/ # 工具库
└── constants/ # 常量定义
版本管理策略
javascript
// 版本号管理
const versionStrategy = {
// 主版本:不兼容的 API 修改
major: 'breaking-changes',
// 次版本:向后兼容的功能性新增
minor: 'new-features',
// 修订版本:向后兼容的问题修正
patch: 'bug-fixes'
};
// 应用版本配置
const appVersions = {
'user-app': '~2.1.0', // 允许小版本更新
'order-app': '^1.0.0', // 允许次版本和修订版本更新
'common-lib': '1.0.0' // 固定版本
};
总结
微前端的核心价值总结
微前端架构为现代前端开发带来的核心价值:
- 解耦大型应用:将复杂单体拆分为可管理的模块
- 提升团队效率:团队可以独立开发、测试、部署
- 技术灵活性:支持渐进式升级和技术栈多样性
- 降低维护成本:每个应用代码量可控,职责清晰
- 快速响应变化:可以快速迭代和试错
选择方案的决策树
项目需求分析
|
├─ 需要强隔离(第三方应用、安全) → iframe
│
├─ 改造现有项目(希望零改造) → wujie
│
├─ 大型复杂应用、多团队协作 → qiankun
│
├─ 中小型项目、追求简单 → micro-app
│
└─ 深度集成、共享组件库 → Module Federation