使用 AtomCode 构建微前端架构:从设计到实现实战
一、什么是微前端
微前端是一种将大型前端应用拆分为多个独立子应用的架构模式,每个子应用可以独立开发、测试和部署,最终通过主应用集成在一起。
核心优势:
- 技术栈无关:各子应用可选用不同技术栈(React、Vue、Angular)
- 独立开发部署:各团队独立工作,互不影响
- 渐进式迁移:支持从单体应用逐步迁移到微前端架构
二、微前端架构设计
2.1 架构模式选择
常见的微前端实现模式有以下几种:
| 模式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| iframe | 完全隔离,实现简单 | 性能开销大,通信复杂 | 简单场景、第三方集成 |
| Single-SPA | 轻量,成熟稳定 | 共享依赖管理复杂 | 中大型项目 |
| Module Federation | 运行时共享,按需加载 | 配置复杂 | 现代化架构 |
| qiankun | 开箱即用,生态完善 | 有一定学习成本 | 企业级项目 |
技术选型:本文使用 qiankun 作为微前端框架,主应用使用 React,子应用支持 React、Vue、Angular 多技术栈。
2.2 整体架构图
┌─────────────────────────────────────────────────────────────┐
│ 主应用 (Main App) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Layout │ │ Router │ │ Store │ │
│ │ (React) │ │ (React) │ │ (Redux) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
└─────────┼────────────────┼────────────────┼────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ 子应用 (Micro Apps) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ User App │ │ Product │ │ Order App │ │
│ │ (React) │ │ App (Vue) │ │ (Angular) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
│ │ │
└────────────────┼────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ 共享模块 (Shared) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ UI Kit │ │ Utils │ │ API SDK │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
三、主应用实现
3.1 主应用入口配置
typescript
import { registerMicroApps, start } from 'qiankun';
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import App from './App';
// 注册微应用
registerMicroApps([
{
name: 'user-app',
entry: '//localhost:8081',
container: '#micro-app-container',
activeRule: '/user',
props: { appId: 'user-app' }
},
{
name: 'product-app',
entry: '//localhost:8082',
container: '#micro-app-container',
activeRule: '/product',
props: { appId: 'product-app' }
},
{
name: 'order-app',
entry: '//localhost:8083',
container: '#micro-app-container',
activeRule: '/order',
props: { appId: 'order-app' }
}
]);
// 启动微前端
start({
sandbox: {
strictStyleIsolation: true,
experimentalStyleIsolation: true
},
prefetch: true
});
// 渲染主应用
const root = ReactDOM.createRoot(document.getElementById('root')!);
root.render(
<React.StrictMode>
<BrowserRouter>
<Routes>
<Route path="*" element={<App />} />
</Routes>
</BrowserRouter>
</React.StrictMode>
);
3.2 主应用布局组件
typescript
import { useState } from 'react';
import { Link, Outlet } from 'react-router-dom';
import { Layout, Menu } from 'antd';
const { Header, Content, Sider } = Layout;
function App() {
const [collapsed, setCollapsed] = useState(false);
const menuItems = [
{ key: 'user', label: <Link to="/user">用户管理</Link> },
{ key: 'product', label: <Link to="/product">商品管理</Link> },
{ key: 'order', label: <Link to="/order">订单管理</Link> }
];
return (
<Layout style={{ height: '100vh' }}>
<Header className="header">
<h1>微前端平台</h1>
</Header>
<Layout>
<Sider
collapsible
collapsed={collapsed}
onCollapse={(value) => setCollapsed(value)}
>
<Menu mode="inline" items={menuItems} />
</Sider>
<Content className="content">
<div id="micro-app-container">
<Outlet />
</div>
</Content>
</Layout>
</Layout>
);
}
export default App;
四、子应用实现
4.1 React 子应用
typescript
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import UserList from './pages/UserList';
import UserForm from './pages/UserForm';
let root: ReactDOM.Root | null = null;
// qiankun 生命周期
export async function bootstrap() {
console.log('User app bootstrap');
}
export async function mount(props: { container: HTMLElement }) {
root = ReactDOM.createRoot(props.container.querySelector('#root')!);
root.render(
<React.StrictMode>
<BrowserRouter basename="/user">
<Routes>
<Route path="/" element={<UserList />} />
<Route path="/create" element={<UserForm />} />
</Routes>
</BrowserRouter>
</React.StrictMode>
);
}
export async function unmount() {
root?.unmount();
root = null;
}
// 独立运行入口
if (!window.__POWERED_BY_QIANKUN__) {
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<BrowserRouter>
<UserList />
</BrowserRouter>
</React.StrictMode>
);
}
4.2 Vue 子应用
typescript
import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router';
import App from './App.vue';
import ProductList from './pages/ProductList.vue';
let app = null;
export async function bootstrap() {
console.log('Product app bootstrap');
}
export async function mount(props) {
const router = createRouter({
history: createWebHistory(window.__POWERED_BY_QIANKUN__ ? '/product' : '/'),
routes: [{ path: '/', component: ProductList }]
});
app = createApp(App);
app.use(router);
app.mount(props.container.querySelector('#app'));
}
export async function unmount() {
app?.unmount();
app = null;
}
五、共享模块设计
5.1 共享 UI 组件
typescript
// Button 组件
export function Button({ type = 'primary', children, onClick }) {
const styles = {
primary: { backgroundColor: '#1890ff', color: '#fff' },
secondary: { backgroundColor: '#fff', color: '#666', border: '1px solid #d9d9d9' }
};
return (
<button style={{ ...styles[type], padding: '8px 16px', borderRadius: '4px', cursor: 'pointer' }} onClick={onClick}>
{children}
</button>
);
}
// Card 组件
export function Card({ title, children }) {
return (
<div style={{ background: '#fff', borderRadius: '8px', padding: '20px', border: '1px solid #f0f0f0' }}>
{title && <h3 style={{ marginBottom: '16px', paddingBottom: '12px', borderBottom: '1px solid #f0f0f0' }}>{title}</h3>}
{children}
</div>
);
}
5.2 共享工具函数
typescript
// 日期格式化
export function formatDate(date, format = 'YYYY-MM-DD') {
const d = new Date(date);
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
return format.replace('YYYY', year).replace('MM', month).replace('DD', day);
}
// 防抖函数
export function debounce(fn, delay) {
let timer = null;
return (...args) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
// 生成 UUID
export function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = Math.random() * 16 | 0;
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
}
六、应用间通信
6.1 EventBus 事件总线
typescript
class EventBus {
constructor() {
this.events = new Map();
}
subscribe(eventName, handler) {
if (!this.events.has(eventName)) {
this.events.set(eventName, new Set());
}
this.events.get(eventName).add(handler);
return {
unsubscribe: () => this.events.get(eventName)?.delete(handler)
};
}
publish(eventName, data) {
this.events.get(eventName)?.forEach(handler => handler(data));
}
}
export const eventBus = new EventBus();
// 使用示例
// eventBus.subscribe('user:login', (data) => console.log(data));
// eventBus.publish('user:login', { userId: '123' });
6.2 共享状态管理
typescript
class SharedState {
constructor() {
this.state = {};
this.listeners = new Map();
}
set(key, value) {
const oldValue = this.state[key];
this.state[key] = value;
this.listeners.get(key)?.forEach(listener => listener(value, oldValue));
}
get(key) {
return this.state[key];
}
on(key, listener) {
if (!this.listeners.has(key)) {
this.listeners.set(key, new Set());
}
this.listeners.get(key).add(listener);
return { unsubscribe: () => this.listeners.get(key)?.delete(listener) };
}
}
export const sharedState = new SharedState();
七、性能优化策略
7.1 懒加载与预加载
typescript
// 配置预加载策略
start({
prefetch: 'all', // 预加载所有子应用
// 或使用自定义预加载策略
// prefetch: (app) => app.name === 'user-app'
});
// 路由守卫实现按需加载
router.beforeEach((to, from, next) => {
const appName = getAppNameFromRoute(to.path);
if (appName && !window.__MICRO_APPS__?.includes(appName)) {
prefetchApp(appName);
}
next();
});
7.2 资源优化配置
javascript
// Webpack 配置优化
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
},
// Gzip 压缩
plugins: [
new CompressionPlugin({
algorithm: 'gzip',
threshold: 8192
})
]
};
八、CI/CD 部署流程
yaml
name: MicroFrontend CI/CD
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
app: [main-app, user-app, product-app, order-app]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with: { node-version: '20' }
- name: Install dependencies
run: npm ci
working-directory: ./apps/${{ matrix.app }}
- name: Build
run: npm run build
working-directory: ./apps/${{ matrix.app }}
- name: Deploy to CDN
uses: cloudflare/wrangler-action@v3
with:
command: pages deploy ./dist
九、常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 样式冲突 | 子应用样式污染主应用 | 使用 CSS Modules 或 Shadow DOM |
| 依赖冲突 | 不同子应用使用不同版本依赖 | 使用 Module Federation 共享依赖 |
| 通信困难 | 应用间通信复杂 | 使用 EventBus 或共享状态 |
| 性能问题 | 多个应用加载导致性能下降 | 按需加载和预加载策略 |
| 调试困难 | 多应用调试复杂 | 使用 qiankun 调试工具 |
十、最佳实践总结
- 明确模块边界:子应用间保持独立,通过明确定义的接口通信
- 统一共享模块:公共组件和工具通过共享模块统一管理
- 渐进式迁移:逐步迁移,避免一次性大规模重构
- 独立测试部署:每个子应用独立编写测试和部署
- 完善监控体系:建立监控和告警,及时发现问题
结语
微前端架构为大型前端项目提供了更好的可维护性和扩展性。通过合理的架构设计和工具选择,团队可以实现高效协作和快速迭代。
本文为原创内容,基于真实微前端项目开发经验整理。如需转载,请注明出处。