什么是前端微服务

前端微服务(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 ComponentsMicro App

选择合适的微前端方案,构建可扩展的大型前端应用。