什么是前端微服务
前端微服务(Micro Frontends)是将前端应用拆分为多个小型、可独立开发、部署和维护的子应用的架构模式。每个子应用由不同的团队负责,最终组合成完整的大型应用。
核心理念
- 独立开发:每个子应用由不同团队独立开发
- 独立部署:子应用可以单独部署,不影响其他应用
- 技术无关:子应用可以使用不同的技术栈(React、Vue、Angular等)
- 增量升级:可以逐步升级技术栈,无需一次性重构
- 团队自治:每个团队拥有完整的开发和部署权限
适用场景
适合使用前端微服务的场景:
- 大型企业级应用(如电商平台、管理系统)
- 多团队协作的复杂项目
- 需要快速迭代和灵活部署的应用
- 历史遗留系统需要逐步重构
不适合使用前端微服务的场景:
- 小型应用或初创项目
- 单一团队维护的简单项目
- 对性能要求极高的应用(微前端会增加一定性能开销)
主流解决方案
1. Module Federation(Webpack 5)
Webpack 5 原生的模块联邦功能,实现模块级别的动态加载和共享。
优势:
- Webpack 5 原生支持,无需额外依赖
- 运行时动态加载
- 支持共享依赖(避免重复打包)
- 技术栈无关
基本配置:
host 应用(主应用):
// webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'host',
remotes: {
// 远程应用配置
reactApp: 'reactApp@http://localhost:3001/remoteEntry.js',
vueApp: 'vueApp@http://localhost:3002/remoteEntry.js',
},
shared: {
// 共享依赖
vue: { singleton: true },
react: { singleton: true },
'react-dom': { singleton: true },
},
}),
],
}
remote 应用(子应用):
// webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'reactApp',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/components/Button',
'./App': './src/App',
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true },
},
}),
],
}
使用远程模块:
// 主应用中使用
import React from 'react'
async function loadRemoteComponent() {
const { default: Button } = await import('reactApp/Button')
return Button
}
function App() {
const [Button, setButton] = React.useState(null)
React.useEffect(() => {
loadRemoteComponent().then(setButton)
}, [])
return <div>{Button && <Button>远程按钮</Button>}</div>
}
Vue 3 + Module Federation:
// vue.config.js
const { defineConfig } = require('@vue/cli-service')
const { ModuleFederationPlugin } = require('webpack').container
module.exports = defineConfig({
configureWebpack: {
plugins: [
new ModuleFederationPlugin({
name: 'vueApp',
filename: 'remoteEntry.js',
exposes: {
'./Counter': './src/components/Counter.vue',
},
shared: {
vue: { singleton: true },
},
}),
],
},
})
2. qiankun(蚂蚁金服)
基于 single-spa 的微前端框架,开箱即用,适合 Vue 和 React 项目。
优势:
- 开箱即用,配置简单
- HTML entry 方式接入,对子应用无侵入
- 支持沙箱隔离(JS/CSS 隔离)
- 支持预加载
安装:
npm install qiankun
主应用配置:
// src/main.js
import { registerMicroApps, start } from 'qiankun'
// 注册子应用
registerMicroApps([
{
name: 'reactApp',
entry: '//localhost:7100',
container: '#subapp-container',
activeRule: '/react',
},
{
name: 'vueApp',
entry: '//localhost:7101',
container: '#subapp-container',
activeRule: '/vue',
},
])
// 启动 qiankun
start()
主应用路由配置:
// src/routerApp.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{ path: '/', component: Home },
{ path: '/react', component: () => null }, // 空组件,由 qiankun 接管
{ path: '/vue', component: () => null },
]
export const router = createRouter({
history: createWebHistory(),
routes,
})
子应用配置(Vue 3):
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
let app = null
function render(props = {}) {
app = createApp(App)
app.use(router)
app.mount(props.container ? props.container.querySelector('#app') : '#app')
}
if (!window.__POWERED_BY_QIANKUN__) {
render()
}
export async function bootstrap() {
console.log('子应用 bootstrap')
}
export async function mount(props) {
console.log('子应用 mount', props)
render(props)
}
export async function unmount(props) {
console.log('子应用 unmount', props)
app.unmount()
}
子应用配置(Vue CLI):
// vue.config.js
module.exports = {
devServer: {
port: 7101,
headers: {
'Access-Control-Allow-Origin': '*',
},
},
configureWebpack: {
output: {
library: `vueApp`,
libraryTarget: 'umd',
},
},
}
3. single-spa
qiankun 的底层依赖,提供更底层的控制能力。
优势:
- 底层框架,灵活性强
- 技术栈完全无关
- 支持多种加载方式
示例:
// root-config.js
import { registerApplication, start } from 'single-spa'
async function loadVueApp() {
return System.import('vueApp')
}
registerApplication({
name: 'vueApp',
app: loadVueApp,
activeWhen: location => location.pathname.startsWith('/vue'),
customProps: { authToken: 'xxx' },
})
start()
4. Micro App(京东)
基于 Web Components 的微前端框架,类似于 Web Components 思路。
优势:
- 基于 Web Components,原生支持
- 侵入性极小
- 支持样式隔离
示例:
// 主应用
import microApp from '@micro-zoe/micro-app'
;<micro-app name="my-app" url="http://localhost:3000" iframe destroy></micro-app>
架构设计最佳实践
1. 应用拆分策略
按业务领域拆分:
电商平台:
├── 主应用 (Host)
│ ├── 路由
│ ├── 布局
│ └── 通用组件
├── 商品微应用 (Product)
│ ├── 商品列表
│ ├── 商品详情
│ └── 搜索
├── 订单微应用 (Order)
│ ├── 订单列表
│ ├── 订单详情
│ └── 支付
└── 用户微应用 (User)
├── 用户信息
├── 地址管理
└── 优惠券
按功能模块拆分:
管理系统:
├── 主应用
├── 用户管理
├── 权限管理
├── 数据统计
└── 系统设置
2. 通信机制
基于 URL 传参:
// 子应用 A 传递参数
history.push('/user/detail?userId=123')
// 子应用 B 接收参数
const userId = new URLSearchParams(location.search).get('userId')
基于 Props 传递:
// qiankun
registerMicroApps([
{
name: 'vueApp',
entry: '//localhost:7101',
container: '#subapp-container',
activeRule: '/vue',
props: {
// 传递给子应用的数据
data: { userId: '123' },
// 传递方法
actions: {
sayHello: () => console.log('Hello'),
},
},
},
])
基于自定义事件:
// 子应用 A 发送事件
window.dispatchEvent(new CustomEvent('user-update', { detail: { userId: '123' } }))
// 子应用 B 监听事件
window.addEventListener('user-update', e => {
console.log('用户更新:', e.detail.userId)
})
基于共享状态管理:
// 使用 Pinia 共享状态
import { createPinia } from 'pinia'
const pinia = createPinia()
// 主应用初始化
app.use(pinia)
// 子应用使用
import { useSharedStore } from '@/stores/shared/store'
const store = useSharedStore()
store.setData({ userId: '123' })
3. 样式隔离
CSS Modules / Scoped CSS:
<style scoped>
.container {
color: red;
}
</style>
CSS 命名空间:
/* 子应用 A */
.app-a .container {
color: red;
}
/* 子应用 B */
.app-b .container {
color: blue;
}
Shadow DOM:
class MyComponent extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
}
connectedCallback() {
this.shadowRoot.innerHTML = `
<style>
.container { color: red; }
</style>
<div class="container">Shadow DOM 内容</div>
`
}
}
customElements.define('my-component', MyComponent)
4. 公共依赖共享
Module Federation 共享依赖:
// webpack.config.js
new ModuleFederationPlugin({
shared: {
// 单例模式,所有应用共享同一实例
vue: { singleton: true, eager: true },
react: { singleton: true },
// 共享 UI 组件库
'element-plus': { singleton: true },
antd: { singleton: true },
// 共享工具库
lodash: { singleton: true },
axios: { singleton: true },
},
})
开发调试
1. 本地开发
启动多个子应用:
# 终端 1 - 主应用
cd host-app
npm run dev
# 终端 2 - React 子应用
cd react-app
npm run dev
# 终端 3 - Vue 子应用
cd vue-app
npm run dev
2. 环境变量配置
// .env.development
VITE_APP_REACT_ENTRY=http://localhost:7100
VITE_APP_VUE_ENTRY=http://localhost:7101
// .env.production
VITE_APP_REACT_ENTRY=https://react.example.com
VITE_APP_VUE_ENTRY=https://vue.example.com
3. 热更新
配置开发环境支持热更新:
// qiankun
start({
sandbox: {
strictStyleIsolation: false,
experimentalStyleIsolation: false,
},
prefetch: 'all',
})
4. 调试工具
Chrome DevTools:
- 使用
console.log打印应用生命周期 - 检查 DOM 结构和样式隔离
- 使用 Vue DevTools / React DevTools 调试子应用
部署策略
1. 独立部署
每个子应用独立部署到不同的域名或路径:
https://example.com/ # 主应用
https://react.example.com/ # React 子应用
https://vue.example.com/ # Vue 子应用
2. 同域部署
所有子应用部署到同一域名不同路径:
https://example.com/ # 主应用
https://example.com/react/ # React 子应用
https://example.com/vue/ # Vue 子应用
Nginx 配置:
server {
listen 80;
server_name example.com;
# 主应用
location / {
root /var/www/host;
try_files $uri $uri/ /index.html;
}
# React 子应用
location /react {
alias /var/www/react;
try_files $uri $uri/ /react/index.html;
}
# Vue 子应用
location /vue {
alias /var/www/vue;
try:files $uri $uri/ /vue/index.html;
}
}
3. 容器化部署
使用 Docker 容器化部署:
# React 子应用 Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=0 /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Kubernetes 部署:
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: react-app
spec:
replicas: 3
selector:
matchLabels:
app: react-app
template:
metadata:
labels:
app: react-app
spec:
containers:
- name: react-app
image: react-app:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: react-app-service
spec:
selector:
app: react-app
ports:
- port: 80
targetPort: 80
type: LoadBalancer
性能优化
1. 预加载
// qiankun 预加载
start({
prefetch: 'all', // 预加载所有子应用
// 或者
prefetch: ['reactApp', 'vueApp'], // 预加载指定子应用
})
2. 懒加载
// 按需加载子应用
registerMicroApps([
{
name: 'vueApp',
entry: '//localhost:7101',
container: '#subapp-container',
activeRule: location => location.pathname.startsWith('/vue'),
activeRule: '/vue',
},
])
3. 缓存策略
// 配置子应用缓存
const apps = [
{
name: 'vueApp',
entry: '//localhost:7101',
container: '#subapp-container',
activeRule: '/vue',
// 缓存配置
cacheLifetime: {
vueApp: 1000 * 60 * 10, // 缓存 10 分钟
},
},
]
4. 代码分割
// webpack.config.js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10,
},
common: {
name: 'common',
minChunks: 2,
priority: 5,
reuseExistingChunk: true,
},
},
},
},
}
最佳实践总结
1. 项目结构
micro-frontend-project/
├── host-app/ # 主应用
│ ├── src/
│ ├── package.json
│ └── vite.config.js
├── react-app/ # React 子应用
│ ├── src/
│ ├── package.json
│ └── webpack.config.js
├── vue-app/ # Vue 子应用
│ ├── src/
│ ├── package.json
│ └── vue.config.js
└── shared/ # 共享代码
├── types/ # 共享类型
├── utils/ # 共享工具
└── components/ # 共享组件
2. 版本管理
{
"name": "host-app",
"version": "1.0.0",
"dependencies": {
"vue": "^3.4.0"
}
}
使用语义化版本管理,确保兼容性。
3. CI/CD 流程
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build and Deploy
run: |
npm ci
npm run build
npm run deploy
4. 监控和日志
// 子应用生命周期钩子
export async function mount(props) {
console.log('[Vue App] mount', props)
// 发送监控数据
analytics.track('app_mounted', { app: 'vue-app' })
}
export async function unmount(props) {
console.log('[Vue App] unmount', props)
analytics.track('app_unmounted', { app: 'vue-app' })
}
常见问题
Q: 如何处理子应用之间的路由冲突?
A: 使用基路径(base URL)隔离路由:
// Vue 子应用
const router = createRouter({
history: createWebHistory('/vue'), // 基路径
routes,
})
// React 子应用
const router = createBrowserRouter({
basename: '/react',
routes,
})
Q: 如何共享全局状态?
A: 使用状态管理库 + 自定义事件:
// 共享状态
import { defineStore } from 'pinia'
export const useSharedStore = defineStore('shared', {
state: () => ({
user: null,
token: null,
}),
})
Q: 如何处理跨域问题?
A: 配置 CORS 或使用代理:
// webpack.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://api.example.com',
changeOrigin: true,
},
},
},
}
Q: 如何实现主题切换?
A: 使用 CSS 变量 + 共享状态:
:root {
--primary-color: #1890ff;
--text-color: #333;
}
// 共享主题状态
const sharedStore = useSharedStore()
sharedStore.setTheme('dark')
Q: 微前端会增加多少性能开销?
A: 相比单体应用:
- 首次加载增加 200-500ms(框架加载)
- 运行时开销 < 5%(通过共享依赖和懒加载优化)
- 可通过预加载、缓存策略进一步优化
参考资源
官方文档
实战项目
学习资源
总结
前端微服务架构为大型应用提供了灵活的解决方案:
优势:
- 独立开发和部署,提高团队效率
- 技术栈无关,渐进式升级
- 代码隔离,减少冲突
挑战:
- 架构复杂度增加
- 性能开销需要优化
- 需要良好的工程化支持
推荐方案:
| 场景 | 推荐方案 |
|---|---|
| Vue/React 项目 | qiankun |
| Webpack 5 项目 | Module Federation |
| 需要底层控制 | single-spa |
| Web Components | Micro App |
选择合适的微前端方案,构建可扩展的大型前端应用。