"这个项目太大了,每次发布都提心吊胆!"三个月前,我们的电商平台已经膨胀到 30 万行代码,构建时间超过 15 分钟,任何小改动都可能影响整个系统。作为技术负责人,我决定带领团队进行微前端改造。今天,我想分享这个过程中的经验和教训。🏗️
为什么需要微前端
首先看看我们遇到的问题:
// 原有的单体应用结构
src/
├── pages/
│ ├── order/ // 订单系统 (5万行)
│ ├── product/ // 商品系统 (8万行)
│ ├── user/ // 用户中心 (4万行)
│ └── admin/ // 后台管理 (13万行)
├── components/ // 公共组件 (3万行)
├── utils/ // 工具函数 (2万行)
└── store/ // 全局状态 (5万行)
主要痛点:
- 构建时间长
- 团队协作困难
- 技术栈更新受限
- 发布风险大
微前端架构设计
1. 基础架构搭建
使用 Module Federation 实现应用间的代码共享:
// webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'container',
remotes: {
order: 'order@http://localhost:3001/remoteEntry.js',
product: 'product@http://localhost:3002/remoteEntry.js',
user: 'user@http://localhost:3003/remoteEntry.js',
admin: 'admin@http://localhost:3004/remoteEntry.js'
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true }
}
})
]
};
2. 应用路由设计
实现了一个智能的路由系统:
// router/index.ts
interface MicroApp {
name: string;
entry: string;
activeRule: string;
container: string;
}
const apps: MicroApp[] = [
{
name: 'order',
entry: '//localhost:3001',
activeRule: '/order',
container: '#micro-container'
},
// ... 其他应用配置
];
class MicroRouter {
private apps: MicroApp[] = [];
constructor(apps: MicroApp[]) {
this.apps = apps;
window.addEventListener('popstate', this.handleRouteChange);
}
private async loadApp(app: MicroApp) {
const container = document.querySelector(app.container);
if (!container) return;
try {
// 动态加载微应用
const { mount, unmount } = await System.import(app.entry);
await mount(container);
return () => unmount(container);
} catch (error) {
console.error(`Failed to load ${app.name}:`, error);
}
}
private handleRouteChange = () => {
const path = window.location.pathname;
const app = this.apps.find(app =>
path.startsWith(app.activeRule)
);
if (app) {
this.loadApp(app);
}
}
}
3. 通信机制设计
实现了一个事件总线来处理应用间通信:
// utils/eventBus.ts
class EventBus {
private events: Map<string, Function[]> = new Map();
on(event: string, callback: Function) {
if (!this.events.has(event)) {
this.events.set(event, []);
}
this.events.get(event)!.push(callback);
}
emit(event: string, data?: any) {
if (!this.events.has(event)) return;
this.events.get(event)!.forEach(callback => {
try {
callback(data);
} catch (error) {
console.error(`Error in event ${event}:`, error);
}
});
}
off(event: string, callback: Function) {
if (!this.events.has(event)) return;
const callbacks = this.events.get(event)!;
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
}
}
}
export const eventBus = new EventBus();
// 使用示例
// 订单系统
eventBus.emit('orderCreated', { orderId: '123' });
// 商品系统
eventBus.on('orderCreated', (data) => {
updateInventory(data.orderId);
});
4. 公共依赖管理
创建了一个共享依赖加载器:
// utils/dependencyLoader.ts
interface Dependency {
name: string;
version: string;
url: string;
}
class DependencyLoader {
private loaded: Set<string> = new Set();
private loading: Map<string, Promise<void>> = new Map();
async load(dep: Dependency): Promise<void> {
const key = `${dep.name}@${dep.version}`;
if (this.loaded.has(key)) return;
if (this.loading.has(key)) {
return this.loading.get(key);
}
const promise = new Promise<void>((resolve, reject) => {
const script = document.createElement('script');
script.src = dep.url;
script.onload = () => {
this.loaded.add(key);
resolve();
};
script.onerror = reject;
document.head.appendChild(script);
});
this.loading.set(key, promise);
return promise;
}
}
// 使用示例
const loader = new DependencyLoader();
await loader.load({
name: 'lodash',
version: '4.17.21',
url: 'https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js'
});
5. 性能优化
实现了预加载策略:
// utils/prefetch.ts
class Prefetcher {
private prefetchedApps: Set<string> = new Set();
async prefetch(apps: MicroApp[]) {
const prefetchPromises = apps.map(async app => {
if (this.prefetchedApps.has(app.name)) return;
try {
// 创建 link 标签进行预加载
const link = document.createElement('link');
link.rel = 'prefetch';
link.href = app.entry;
document.head.appendChild(link);
this.prefetchedApps.add(app.name);
} catch (error) {
console.error(`Failed to prefetch ${app.name}:`, error);
}
});
await Promise.all(prefetchPromises);
}
prefetchOnIdle() {
if ('requestIdleCallback' in window) {
requestIdleCallback(() => this.prefetch(apps));
} else {
setTimeout(() => this.prefetch(apps), 3000);
}
}
}
实践效果
经过三个月的改造,我们取得了显著的成效:
构建时间:
- 主应用:3分钟
- 子应用:平均2分钟
首屏加载:
- 改造前:5s
- 改造后:2s
团队协作:
- 4个团队可以独立开发部署
- 技术栈可以灵活选择
代码量:
- 每个子应用控制在5万行以内
- 复用组件数量提升50%
经验总结
- 技术选型建议:
interface TechStack { moduleFederation: boolean; // 推荐使用 singleSpa: boolean; // 特定场景考虑 iframe: boolean; // 不推荐 }
const recommendations: Record<string, string> = { 新项目: 'Module Federation', 遗留系统: 'Single-spa', 快速验证: 'Iframe' };
2. 拆分原则:
```typescript
interface SplitStrategy {
byDomain: boolean; // 按业务域拆分
byTeam: boolean; // 按团队拆分
byFrequency: boolean; // 按更新频率拆分
}写在最后
微前端不是银弹,关键是要:
- 合理评估收益成本
- 循序渐进地改造
- 建立统一的技术标准
- 重视团队协作
有什么问题欢迎在评论区讨论,我们一起学习进步!
如果觉得有帮助,别忘了点赞关注,我会继续分享更多实战经验~
616

被折叠的 条评论
为什么被折叠?



