前端微服务实战:大型应用的拆分与治理

发布于:2024-12-20 ⋅ 阅读:(80) ⋅ 点赞:(0)

"这个系统已经无法维护了..."周五的架构评审会上,我盯着屏幕上那张错综复杂的依赖关系图发愁。作为一个运行了三年的企业级中后台系统,代码量已经超过 50 万行,构建时间长达 40 分钟,任何修改都可能引发连锁反应。

更让人头疼的是,随着业务的快速发展,不同业务线之间的耦合越来越严重。一个小小的样式修改都可能影响到其他模块的展示。经过团队讨论,我们决定采用微前端架构对系统进行拆分重构。

现状分析

首先梳理了系统目前面临的主要问题:

  • 构建部署效率低下
  • 团队协作成本高
  • 技术栈难以升级
  • 代码复用困难
  • 线上问题难以隔离

就像一座不断扩建的大楼,没有合理的分区和规划,最终变得杂乱无章。我们需要把这座大楼重新规划成一个个独立又互联的空间。

架构设计

经过调研和验证,我们选择了基于 single-spa 的微前端方案:

// 基座应用
import { registerApplication, start } from 'single-spa'

// 注册子应用
const registerMicroApp = (name: string, entry: string) => {
  registerApplication({
    name,
    app: async () => {
      // 动态加载子应用
      const module = await System.import(entry)
      return module.default
    },
    activeWhen: location => {
      // 基于路由匹配激活子应用
      return location.pathname.startsWith(`/${name}`)
    }
  })
}

// 子应用配置
const microApps = [
  {
    name: 'crm',
    entry: '//localhost:3001/main.js',
    activeRule: '/crm'
  },
  {
    name: 'erp',
    entry: '//localhost:3002/main.js',
    activeRule: '/erp'
  },
  {
    name: 'dashboard',
    entry: '//localhost:3003/main.js',
    activeRule: '/dashboard'
  }
]

// 注册所有子应用
microApps.forEach(app => registerMicroApp(app.name, app.entry))

// 启动微前端框架
start()

子应用改造

每个子应用需要暴露生命周期钩子:

// 子应用入口
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore } from './store'

// 导出生命周期钩子
export async function bootstrap() {
  console.log('应用启动中...')
}

export async function mount(props) {
  const { container, globalStore } = props
  const store = createStore(globalStore)

  ReactDOM.render(
    <Provider store={store}>
      <App />
    </Provider>,
    container
  )
}

export async function unmount(props) {
  const { container } = props
  ReactDOM.unmountComponentAtNode(container)
}

通信方案

子应用间的通信是一个关键问题,我们实现了一个事件总线:

// utils/eventBus.ts
class EventBus {
  private events = new Map<string, Function[]>()

  // 订阅事件
  on(event: string, callback: Function) {
    if (!this.events.has(event)) {
      this.events.set(event, [])
    }
    this.events.get(event).push(callback)

    // 返回取消订阅函数
    return () => {
      const callbacks = this.events.get(event)
      const index = callbacks.indexOf(callback)
      callbacks.splice(index, 1)
    }
  }

  // 发布事件
  emit(event: string, data?: any) {
    if (!this.events.has(event)) return
    this.events.get(event).forEach(callback => {
      try {
        callback(data)
      } catch (error) {
        console.error(`事件处理错误: ${event}`, error)
      }
    })
  }
}

export const eventBus = new EventBus()

样式隔离

为了避免样式冲突,我们采用了 CSS Modules 和动态前缀:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: {
                localIdentName: '[name]__[local]___[hash:base64:5]'
              }
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              plugins: [
                require('postcss-prefix-selector')({
                  prefix: `[data-app="${process.env.APP_NAME}"]`
                })
              ]
            }
          }
        ]
      }
    ]
  }
}

性能优化

微前端架构下的性能优化主要从这几个方面入手:

// 预加载策略
const prefetchApps = async () => {
  const nextPossibleApps = predictNextApps()

  nextPossibleApps.forEach(app => {
    const script = document.createElement('link')
    script.rel = 'prefetch'
    script.href = app.entry
    document.head.appendChild(script)
  })
}

// 共享依赖
const sharedDependencies = {
  react: {
    singleton: true,
    requiredVersion: '^17.0.0'
  },
  'react-dom': {
    singleton: true,
    requiredVersion: '^17.0.0'
  },
  'react-router-dom': {
    singleton: true,
    requiredVersion: '^5.2.0'
  }
}

实践效果

经过三个月的改造,我们取得了显著的成效:

  • 构建时间从40 分钟减少到 5 分钟
  • 各团队可以独立开发部署
  • 线上问题可以快速定位和修复
  • 新技术栈的尝试变得容易

最让我印象深刻的是一位同事说:"现在终于可以专注于业务开发,不用担心影响到其他团队了。"

经验总结

微前端架构就像城市规划,需要统筹兼顾又要保持灵活。我们的经验是:

合理分层 - 基座应用要足够稳定清晰边界 - 子应用之间要解耦统一规范 - 公共依赖要严格管理持续优化 - 性能问题要重点关注

写在最后

微前端不是银弹,它更像是一把双刃剑。使用得当可以大幅提升开发效率,但也会带来一定的复杂性。关键是要在架构设计时充分权衡,在实施过程中严格把控。

有什么问题欢迎在评论区讨论,让我们一起探讨微前端架构的最佳实践!

如果觉得有帮助,别忘了点赞关注,我会继续分享更多实战经验~


网站公告

今日签到

点亮在社区的每一天
去签到