微前端架构实践:基于Wujie的模块化设计

微前端架构实践:基于Wujie的模块化设计

概述

微前端架构允许将前端应用拆分为多个独立部署的子应用,每个子应用可由不同团队使用不同技术栈开发。本文将介绍如何基于腾讯无界(Wujie)框架实现微前端架构,实现控制台级别的模块化和独立部署。

为什么选择微前端

传统单体前端的痛点

  1. 代码库庞大:随着功能增加,代码量指数增长
  2. 构建时间长:修改一个小功能需要重新构建整个应用
  3. 团队协作冲突:多团队在同一代码库中开发容易产生冲突
  4. 技术栈锁定:难以逐步引入新技术
  5. 部署风险高:任何小错误都可能影响整个平台

微前端的优势

微前端架构

架构设计

整体架构

┌─────────────────────────────────────────────────────────────────────────────┐
│                        微前端架构设计                                        │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                         主应用 (console)                             │   │
│  │  ┌──────────────────────────────────────────────────────────────┐  │   │
│  │  │                      Header (通用导航)                          │  │   │
│  │  │  Logo    [App A] [App B] [App C] [App D]        User Menu    │  │   │
│  │  └──────────────────────────────────────────────────────────────┘  │   │
│  │  ┌──────────────────────────────────────────────────────────────┐  │   │
│  │  │                                                              │  │   │
│  │  │              子应用渲染区域 (<WujieReact/>)                   │  │   │
│  │  │                                                              │  │   │
│  │  │   ┌─────────────────────────────────────────────────────┐   │  │   │
│  │  │   │                                                     │   │  │   │
│  │  │   │              ucenter.console                        │   │  │   │
│  │  │   │           (用户与权限管理控制台)                      │   │  │   │
│  │  │   │                                                     │   │  │   │
│  │  │   └─────────────────────────────────────────────────────┘   │  │   │
│  │  │                                                              │  │   │
│  │  └──────────────────────────────────────────────────────────────┘  │   │
│  │                                                                     │   │
│  │  职责:                                                              │   │
│  │  - 统一导航和布局                                                    │   │
│  │  - 子应用生命周期管理                                                 │   │
│  │  - 全局状态共享(用户、权限)                                          │   │
│  │  - 公共组件和样式                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                         子应用 (独立仓库/独立部署)                    │   │
│  │                                                                     │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐               │   │
│  │  │ ucenter.     │  │   app.       │  │   rag.       │               │   │
│  │  │  console     │  │  console     │  │  console     │               │   │
│  │  │ (用户中心)    │  │ (应用管理)   │  │ (知识库)     │               │   │
│  │  │ Umi Max      │  │ Umi Max      │  │ Umi Max      │               │   │
│  │  └──────────────┘  └──────────────┘  └──────────────┘               │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐               │   │
│  │  │statistic.    │  │pdfviewer.    │  │pptonline.    │               │   │
│  │  │  console     │  │  console     │  │  console     │               │   │
│  │  │ (统计分析)   │  │ (PDF控制台)  │  │ (在线PPT)    │               │   │
│  │  │ Umi Max      │  │ Umi Max      │  │ Umi Max      │               │   │
│  │  └──────────────┘  └──────────────┘  └──────────────┘               │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

为什么选择Wujie

框架 隔离方式 技术栈兼容 性能 使用复杂度
qiankun JS Sandbox + Proxy 良好 中等 中等
micro-app WebComponent 良好
Wujie iframe + WebComponent 优秀 优秀
Module Federation 模块联邦 需同源

Wujie的优势:

  • 真正的JS/CSS隔离(iframe天然隔离)
  • 无需修改子应用代码(无侵入性)
  • 支持vite等现代构建工具
  • 子应用保活(切换不重新加载)
  • 原生兼容(iframe内完全独立)

主应用实现

1. 项目结构

console/                         # 主控制台
├── src/
│   ├── app.tsx                 # Umi应用配置
│   ├── layouts/
│   │   └── index.tsx           # 主布局(包含导航)
│   ├── pages/
│   │   ├── index.tsx           # 首页
│   │   └── Apps/               # 子应用容器
│   │       └── Jump.tsx        # 子应用跳转页
│   ├── components/
│   │   ├── AppIcon/            # 应用图标组件
│   │   ├── GlobalHeader/       # 全局头部
│   │   └── SubAppContainer/    # 子应用容器包装
│   └── services/
│       └── app.ts              # 应用信息服务
├── config/
│   └── config.ts               # Umi配置
└── package.json

2. 主应用配置

// src/app.tsx
import { RuntimeConfig } from '@umijs/max'

export const layout: RuntimeConfig['layout'] = () => {
  return {
    title: '微服务平台',
    logo: '/logo.png',
    menu: {
      locale: false,
    },
    layout: 'mix',  // 混合布局
    splitMenus: true,
    contentWidth: 'Fluid',
    navTheme: 'light',
    primaryColor: '#1890ff',
  }
}

// 初始化应用列表
export async function getInitialState(): Promise<{
  currentUser?: API.CurrentUser
  apps?: API.AppInfo[]
}> {
  const apps = await fetchAppList()
  const currentUser = await fetchCurrentUser()
  
  return {
    currentUser,
    apps
  }
}

3. 子应用路由配置

// config/routes.ts
export default [
  {
    path: '/',
    redirect: '/home',
  },
  {
    path: '/home',
    name: '首页',
    component: './Home',
  },
  // 子应用路由 - 动态生成
  {
    path: '/app/:clientCode/*',
    component: './Apps/Jump',
    hideInMenu: true,
  },
]

4. 子应用容器组件

// src/pages/Apps/Jump.tsx
import React, { useEffect, useState } from 'react'
import { useParams, history } from '@umijs/max'
import WujieReact from 'wujie-react'
import { Spin, Result } from 'antd'
import { useModel } from '@umijs/max'

const { bus, preloadApp, destroyApp } = WujieReact

const SubAppContainer: React.FC = () => {
  const { clientCode } = useParams<{ clientCode: string }>()
  const { initialState } = useModel('@@initialState')
  const [appInfo, setAppInfo] = useState<API.AppInfo | null>(null)
  const [loading, setLoading] = useState(true)
  
  // 获取子应用信息
  useEffect(() => {
    const app = initialState?.apps?.find(a => a.clientCode === clientCode)
    if (app) {
      setAppInfo(app)
    } else {
      history.push('/404')
    }
    setLoading(false)
  }, [clientCode])
  
  if (loading) {
    return <Spin size="large" style={{ marginTop: 100 }} />
  }
  
  if (!appInfo) {
    return (
      <Result
        status="404"
        title="应用不存在"
        subTitle={`未找到客户端代码为 "${clientCode}" 的应用`}
      />
    )
  }
  
  return (
    <WujieReact
      width="100%"
      height="100%"
      name={clientCode}                    // 应用唯一标识
      url={appInfo.url}                   // 子应用URL
      sync={true}                         // 同步路由
      fetch={fetch}                       // 自定义fetch(用于鉴权)
      props={{
        // 传递给子应用的属性
        token: localStorage.getItem('token'),
        userInfo: initialState?.currentUser,
        clientCode: appInfo.clientCode,
      }}
      // 生命周期钩子
      beforeLoad={() => {
        console.log(`[Wujie] ${clientCode} 开始加载`)
      }}
      beforeMount={() => {
        console.log(`[Wujie] ${clientCode} 挂载前`)
      }}
      afterMount={() => {
        console.log(`[Wujie] ${clientCode} 挂载完成`)
      }}
      beforeUnmount={() => {
        console.log(`[Wujie] ${clientCode} 卸载前`)
      }}
      afterUnmount={() => {
        console.log(`[Wujie] ${clientCode} 卸载完成`)
      }}
    />
  )
}

export default SubAppContainer

5. 全局导航组件

// src/components/GlobalHeader/RightContent.tsx
import React from 'react'
import { Space, Dropdown, Menu, Avatar, Badge } from 'antd'
import { useModel, history } from '@umijs/max'
import { LogoutOutlined, UserOutlined, SettingOutlined } from '@ant-design/icons'

const GlobalHeaderRight: React.FC = () => {
  const { initialState, setInitialState } = useModel('@@initialState')
  const { currentUser } = initialState || {}
  
  // 应用切换菜单
  const appMenuItems = initialState?.apps?.map(app => ({
    key: app.clientCode,
    icon: <AppIcon icon={app.icon} />,
    label: app.name,
    onClick: () => history.push(`/app/${app.clientCode}`),
  }))
  
  // 用户菜单
  const userMenuItems = [
    {
      key: 'profile',
      icon: <UserOutlined />,
      label: '个人中心',
    },
    {
      key: 'settings',
      icon: <SettingOutlined />,
      label: '系统设置',
    },
    {
      type: 'divider' as const,
    },
    {
      key: 'logout',
      icon: <LogoutOutlined />,
      label: '退出登录',
      onClick: async () => {
        await logout()
        setInitialState({})
        history.push('/login')
      },
    },
  ]
  
  return (
    <Space size={24}>
      {/* 应用切换 */}
      <Dropdown menu={{ items: appMenuItems }} placement="bottomRight">
        <span style={{ cursor: 'pointer' }}>
          <AppstoreOutlined /> 应用中心
        </span>
      </Dropdown>
      
      {/* 通知 */}
      <Badge count={5} size="small">
        <BellOutlined style={{ fontSize: 18 }} />
      </Badge>
      
      {/* 用户菜单 */}
      <Dropdown menu={{ items: userMenuItems }} placement="bottomRight">
        <Space style={{ cursor: 'pointer' }}>
          <Avatar src={currentUser?.avatar} icon={<UserOutlined />} />
          <span>{currentUser?.username}</span>
        </Space>
      </Dropdown>
    </Space>
  )
}

export default GlobalHeaderRight

子应用实现

1. 子应用项目结构

ucenter.console/                  # 用户中心控制台
├── src/
│   ├── app.tsx                  # Umi应用配置
│   ├── pages/
│   │   ├── user/                # 用户管理
│   │   ├── role/                # 角色管理
│   │   ├── permission/          # 权限管理
│   │   └── organization/        # 组织架构
│   └── services/
│       └── api.ts
├── config/
│   └── config.ts
└── package.json

2. 子应用适配Wujie

// src/app.tsx (子应用)
import { RuntimeConfig } from '@umijs/max'

// 判断是否在Wujie环境中运行
const isInWujie = () => {
  return window.__POWERED_BY_WUJIE__
}

// 获取Wujie传递的属性
const getWujieProps = () => {
  if (isInWujie()) {
    return window.$wujie?.props || {}
  }
  return {}
}

export const layout: RuntimeConfig['layout'] = () => {
  // 在Wujie中隐藏头部导航(由主应用提供)
  const wujieProps = getWujieProps()
  
  return {
    title: '用户中心',
    // 在Wujie中不显示Logo和导航
    pure: isInWujie(),
    // 获取主应用传递的token
    requestHeaders: {
      'Authorization': `Bearer ${wujieProps.token}`,
      'X-Client-Code': wujieProps.clientCode,
    },
    // 与主应用通信
    onPageLoad: () => {
      if (isInWujie()) {
        // 通知主应用页面加载完成
        window.$wujie?.bus?.$emit('page-loaded', {
          path: window.location.pathname,
          title: document.title,
        })
      }
    },
  }
}

// 初始化
export async function getInitialState() {
  const wujieProps = getWujieProps()
  
  // 使用主应用传递的用户信息
  if (wujieProps.userInfo) {
    return {
      currentUser: wujieProps.userInfo,
    }
  }
  
  // 独立运行时的逻辑
  return fetchCurrentUser()
}

3. 子应用路由配置

// config/routes.ts (子应用)
export default [
  {
    path: '/',
    redirect: '/user/list',
  },
  {
    path: '/user',
    name: '用户管理',
    routes: [
      {
        path: '/user/list',
        name: '用户列表',
        component: './User/List',
      },
      {
        path: '/user/detail/:id',
        name: '用户详情',
        component: './User/Detail',
        hideInMenu: true,
      },
    ],
  },
  {
    path: '/role',
    name: '角色管理',
    routes: [
      {
        path: '/role/list',
        name: '角色列表',
        component: './Role/List',
      },
    ],
  },
  {
    path: '/permission',
    name: '权限管理',
    component: './Permission',
  },
  {
    path: '/organization',
    name: '组织架构',
    component: './Organization',
  },
]

通信机制

1. 主应用 → 子应用(Props传递)

// 主应用传递数据
<WujieReact
  props={{
    token: localStorage.getItem('token'),
    userInfo: initialState?.currentUser,
    theme: 'light',
    locale: 'zh-CN',
  }}
/>

// 子应用接收数据
const wujieProps = window.$wujie?.props
console.log(wujieProps.token)
console.log(wujieProps.userInfo)

2. 子应用 → 主应用(EventBus)

// 子应用发送消息
if (window.__POWERED_BY_WUJIE__) {
  window.$wujie.bus.$emit('notification', {
    type: 'success',
    message: '用户创建成功',
  })
  
  // 通知路由变化
  window.$wujie.bus.$emit('route-change', {
    path: '/user/list',
    title: '用户列表',
  })
}

// 主应用接收消息
useEffect(() => {
  const handleNotification = (data: any) => {
    notification[data.type]({
      message: data.message,
    })
  }
  
  // 监听子应用事件
  bus.$on('notification', handleNotification)
  
  return () => {
    bus.$off('notification', handleNotification)
  }
}, [])

3. 全局状态共享

// 使用RxJS或Zustand实现跨应用状态共享
import { create } from 'zustand'
import { subscribeWithSelector } from 'zustand/middleware'

// 创建全局状态存储
const useGlobalStore = create(
  subscribeWithSelector(() => ({
    user: null,
    permissions: [],
    theme: 'light',
  }))
)

// 主应用设置状态
useGlobalStore.setState({ user: currentUser })

// 子应用订阅状态变化(通过window共享)
if (window.__POWERED_BY_WUJIE__) {
  // 使用主应用传递的状态
  const globalStore = window.parent.useGlobalStore
  
  globalStore.subscribe(
    (state) => state.user,
    (user) => {
      console.log('User updated:', user)
    }
  )
}

样式隔离

1. CSS Module方案

/* 子应用使用CSS Module */
.userList-module__container {
  padding: 24px;
  background: #fff;
}

.userList-module__title {
  font-size: 20px;
  font-weight: 500;
  margin-bottom: 16px;
}

2. CSS前缀方案

// 子应用配置CSS前缀
export default {
  // ...
  extraPostCSSPlugins: [
    require('postcss-prefix-selector')({
      prefix: '.ucenter-console',  // 子应用专属前缀
      exclude: ['body', 'html'],
    }),
  ],
}

3. Wujie样式隔离

Wujie天然提供CSS隔离,子应用的样式只作用于iframe内部:

<!-- Wujie创建的iframe结构 -->
<wujie-app>
  <iframe src="about:blank">
    #document
    <html>
      <head>
        <!-- 子应用的CSS -->
        <style>/* ucenter.console styles */</style>
      </head>
      <body>
        <!-- 子应用的DOM -->
      </body>
    </html>
  </iframe>
</wujie-app>

部署配置

1. 独立部署流程

# docker-compose.yml (每个子应用独立)
version: '3.8'
services:
  ucenter-console:
    image: reg.example.com/micro/ucenter.console:${VERSION}
    container_name: micro-ucenter-console
    ports:
      - "80"
    networks:
      - micro-net

networks:
  micro-net:
    external: true

2. Nginx配置

# ucenter.console.conf
server {
    listen 80;
    
    location / {
        root /ucenter-console/dist;
        index index.html;
        try_files $uri $uri/ /index.html;
    }
}

3. 主应用动态加载子应用

// 从配置中心获取应用列表
const fetchAppList = async (): Promise<API.AppInfo[]> => {
  const response = await fetch('/api/apps/list')
  const apps = await response.json()
  
  return apps.map(app => ({
    clientCode: app.code,
    name: app.name,
    url: app.deployUrl,  // 如: http://ucenter.console.svc.cluster.local
    icon: app.icon,
    permissions: app.permissions,
  }))
}

性能优化

1. 预加载

// 预加载常用子应用
import { preloadApp } from 'wujie-react'

// 登录后预加载核心应用
useEffect(() => {
  if (currentUser) {
    // 预加载用户中心
    preloadApp({
      name: 'ucenter',
      url: 'http://ucenter.console.svc.cluster.local',
    })
    
    // 预加载应用管理
    preloadApp({
      name: 'app',
      url: 'http://app.console.svc.cluster.local',
    })
  }
}, [currentUser])

2. 保活模式

// 启用保活,切换时不重新加载
<WujieReact
  name={clientCode}
  url={appInfo.url}
  alive={true}  // 保活模式
  // ...
/>

3. 代码分割

// 子应用路由懒加载
const UserList = React.lazy(() => import('./pages/User/List'))
const RoleList = React.lazy(() => import('./pages/Role/List'))

总结

微前端架构设计的关键点:

  1. 选型:Wujie提供优秀的隔离性和易用性
  2. 主应用:负责导航、鉴权、全局状态
  3. 子应用:独立开发、独立部署、技术栈无关
  4. 通信:Props + EventBus机制
  5. 样式:天然隔离 + CSS Module
  6. 部署:每个子应用独立CI/CD

下一篇将介绍在线PPT编辑器的前端技术实现。

阅读更多

Skills系统:可扩展AI能力设计

Skills系统:可扩展AI能力设计

概述 Skills系统是AI-Native架构中的重要组件,它允许通过声明式配置扩展AI的能力。本文将介绍Skills系统的设计与实现,让大模型能够像人类专家一样具备特定领域的能力。 什么是Skills系统 概念 Skills(技能)是一种声明式的AI能力扩展机制,类似于人类的"专业技能": 通用AI助手 专业AI助手(带Skills) ┌──────────────────────┐ ┌──────────────────────────────┐ │ │ │ │ │ 用户:请帮我写代码 │ │ 用户:请帮我审查这段代码 │ │ │ │ │ │ AI:我是一个AI助手 │ │ AI:[激活

By 菱角
插件化架构设计模式

插件化架构设计模式

概述 插件化架构是一种将核心功能与扩展功能分离的设计模式,允许系统在运行时动态加载和卸载功能模块。本文将介绍如何在微服务平台中设计和实现插件化架构。 为什么需要插件化 插件化优势 1. 模块化:功能独立,边界清晰 2. 可扩展:按需加载,动态增删 3. 隔离性:插件间互不干扰 4. 可维护:独立开发、测试、部署 5. 可定制:用户按需选择功能 核心设计 架构概览 核心组件实现 1. 插件接口定义 // core/plugin.interface.ts // 插件接口 export interface IPlugin { // 插件名称 readonly name: string // 插件版本 readonly version: string // 插件配置 getConfig(): PluginConfig // 插件清单

By 菱角
gRPC服务通信设计与实践

gRPC服务通信设计与实践

概述 在微服务架构中,服务间通信是关键环节。相比REST API,gRPC提供了更高的性能和更强的类型安全。本文将介绍如何在微服务平台中设计和实现gRPC服务通信。 为什么选择gRPC gRPC vs REST对比 特性 gRPC REST 协议 HTTP/2 HTTP/1.1 序列化 Protocol Buffers (二进制) JSON (文本) 性能 高(二进制+压缩) 中(文本开销) 类型安全 强(代码生成) 弱(运行时检查) 流式通信 原生支持(双向流) 需额外实现(SSE/WebSocket) 代码生成 自动生成 手动编写 浏览器支持 需gRPC-Web 原生支持 调试难度

By 菱角
多语言微服务架构:Node.js与Python协作

多语言微服务架构:Node.js与Python协作

概述 在微服务架构中,根据场景选择最适合的编程语言是最佳实践。本文将介绍如何在微服务平台中实现Node.js与Python的协作,发挥各自技术优势。 技术选型策略 为什么混合使用 服务划分 Node.js服务(7个) 服务 功能 选择Node.js的原因 llm.api 大模型服务 高并发SSE流式响应 ucenter.api 用户中心 RESTful API标准实践 doc.api 文件服务 流式上传下载处理 resource.api 资源管理 gRPC高性能通信 rag.api 知识库服务 MongoDB集成便利 statistic.api 统计分析 事件驱动架构 pptonline.api PPT服务 与前端技术栈统一 Python服务(1个) 服务 功能 选择Python的原因

By 菱角