Skip to content

本文由 简悦 SimpRead 转码, 原文地址 mp.weixin.qq.com

前公司集团进行了公司收购,并购了一个上市公司的分公司,组织架构整体大调整了。所以,两个公司的一些系统就需要进行整合,比如管理系统及一些模块,要**「快速」「保证稳定性」**的前提下,我们想到了用微前端方案解决。

我们经过了解发现将不同技术栈的系统整合到一起的微前端方案里,只有 qiankun、Module Federation 都是比较符合我们需求的。现在要整合三个不同框架的系统,我们考虑:

「1. 如何选择合适的主应用和子应用?」****「2. 如何解决不同框架之间的冲突,比如全局样式、JavaScript 隔离、路由管理等?」

首先,主应用的选择。通常主应用作为基座,负责整体布局、路由导航和子应用的加载。主应用可以使用任何框架,但需要支持微前端框架的要求。比如 qiankun 推荐主应用使用 React 或者 Vue,但也可以纯 JavaScript。我司项目有 React16 和 Vue2、3。因系统比较繁杂且我们不清楚业务细节,所以我们将主应用独立出来,或者改造其中一个现有的系统作为主应用。最后选择主应用使用 Vue3,社区支持好且团队成员上手更速度。

接下来是子应用的改造。每个子应用需要暴露生命周期钩子,并配置 webpack。对于 Vue2、Vue3 和 React16,每个子应用都需要调整 webpack 配置,确保正确导出生命周期函数。同时,需要注意不同框架可能存在的全局变量污染,比如 Vue 的全局组件、React 的全局状态等,这时候需要沙箱隔离机制。qiankun 的 JS 沙箱和样式隔离是否能有效处理这些问题?

路由管理也是一个重点。主应用需要处理一级路由,子应用处理自己的子路由。需要确保路由切换时正确加载和卸载子应用,避免内存泄漏。比如使用 history 模式路由,主应用和子应用的路由前缀需要配置正确。

公共依赖的处理,比如 Vue 或 React 的版本不同,如何避免冲突?可能需要使用 externals 配置,将公共库从子应用中排除,由主应用统一提供,但不同版本的库可能会有问题。或者利用 webpack 的模块联邦(Module Federation)来共享依赖,但需要确认是否兼容不同版本的框架。

构建和部署方面,子应用需要独立部署,主应用只需加载子应用的入口文件。可能需要配置子应用的打包输出为 umd 格式,并设置 publicPath 为动态的,以适应不同环境。

此外我们可以还需要一些优化措施,比如预加载子应用资源,提升性能;统一的状态管理方案,比如使用 redux 或 vuex 在主应用和子应用之间共享状态;以及错误处理机制,比如子应用加载失败时的降级处理。

最后考虑到现有的项目可能已有一定的配置,qiankun 可能更容易上手,而 Module Federation 需要 webpack5 支持,可能需要升级构建工具,这一系列更繁杂。

有了以上的简单思考后,我们进行了下面的工作,以下是简述:


「一、技术选型对比」

方案
优点
缺点
适用场景
「qiankun」
成熟度高、社区资源丰富、沙箱隔离完善
需手动配置子应用生命周期、依赖管理较复杂
多技术栈混合、快速落地
「Module Federation」
原生 Webpack 支持、依赖共享灵活
需 Webpack5、框架版本需兼容、调试复杂
同构建体系项目、深度定制需求

「推荐选择 qiankun」:更适合混合技术栈快速整合,已有大量生产环境验证


「二、架构设计」

主应用 (基座)
├── 导航系统 (处理全局路由/权限/状态)
├── 子应用容器
│    ├── Vue2 子系统 (独立仓库)
│    ├── Vue3 子系统 (独立仓库)
│    └── React16 子系统 (独立仓库)
└── 公共依赖管理 (共享工具库/样式规范)

「三、具体实施步骤」

「1. 搭建主应用基座」

「技术栈」:Vue3(组合式 API + TypeScript)或纯 JavaScript

// main-app/src/micro-fe-setup.js
import { registerMicroApps, start } from'qiankun';

registerMicroApps([
  {
    name: 'vue2-app',
    entry: '//localhost:7101',
    container: '#subapp-container',
    activeRule: '/vue2',
    props: { authToken: 'xxx' } // 传递全局参数
  },
  {
    name: 'vue3-app',
    entry: '//localhost:7102',
    container: '#subapp-container',
    activeRule: '/vue3'
  },
  {
    name: 'react16-app',
    entry: '//localhost:7103',
    container: '#subapp-container',
    activeRule: '/react16'
  }
]);

start({
prefetch: 'all', // 预加载子应用
sandbox: { 
    experimentalStyleIsolation: true// 开启样式沙箱
  }
});

「2. 子应用改造(Vue2)」

「关键配置」

// vue2-app/src/public-path.js
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

// vue2-app/src/main.js
let instance = null;

functionrender(props = {}) {
const { container } = props;
  instance = new Vue({
    router,
    store,
    render: h => h(App)
  }).$mount(container ? container.querySelector('#app') : '#app');
}

// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

// 暴露qiankun生命周期钩子
exportasyncfunctionbootstrap() {
console.log('[vue2] app bootstraped');
}

exportasyncfunctionmount(props) {
console.log('[vue2] props from main framework', props);
  render(props);
}

exportasyncfunctionunmount() {
  instance.$destroy();
  instance.$el.innerHTML = '';
  instance = null;
}

「Webpack 配置」

// vue2-app/vue.config.js
module.exports = {
devServer: {
    headers: {
      'Access-Control-Allow-Origin': '*'// 允许跨域
    }
  },
configureWebpack: {
    output: {
      library: `vue2App`,
      libraryTarget: 'umd',
      jsonpFunction: `webpackJsonp_vue2App`
    }
  }
};

「3. 解决多框架冲突」

「样式隔离方案」

  • 启用 qiankun 的 experimentalStyleIsolation(添加前缀选择器)

  • 各子应用使用 CSS Modules

  • 主应用提供基础 Reset CSS

「JS 沙箱策略」

// 主应用启动配置
start({
  sandbox: {
    strictStyleIsolation: true, // Shadow DOM隔离
    speedy: false// 兼容IE
  }
});

「公共依赖处理」

// 主应用package.json
{
"sharedDependencies": {
    "lodash": "^4.17.21",
    "axios": "^0.21.1"
  }
}

// 子应用webpack配置
externals: {
'lodash': 'lodash',
'axios': 'axios'
}

「4. 路由统一管理」

「主应用路由配置」

// main-app/src/router.js
const routes = [  {    path: '/vue2/*',    name: 'vue2',    meta: { title: 'Vue2子系统' }  },  {    path: '/vue3/*',    name: 'vue3',    meta: { title: 'Vue3子系统' }  },  {    path: '/react16/*',    name: 'react16',    meta: { title: 'React16子系统' }  }];

「子应用路由改造」(以 React16 为例):

// react16-app/src/App.js
<Routerbasename={window.__POWERED_BY_QIANKUN__ ? '/react16' : '/'}>
  <Switch>
    <Routepath="/detail"component={DetailPage} />
  </Switch>
</Router>

「四、部署优化策略」

  1. 「独立部署」:每个子应用单独构建,主应用通过 Nginx 配置反向代理

    location /vue2 {
      proxy_pass http://vue2-server;
    }
    location /vue3 {
      proxy_pass http://vue3-server;
    }
  2. 「资源预加载」

    start({
      prefetch: (app) => app.name !== 'react16-app'// 按需预加载
    });
  3. 「性能监控」

    // 主应用集成监控SDK
    import { performanceMonitor } from'@monitor/sdk';
    
    performanceMonitor.init({
      apps: ['vue2-app', 'vue3-app', 'react16-app']
    });

「五、常见问题解决方案」

  1. 「样式污染」
  • 使用scoped样式(Vue)或 CSS Modules

  • 主应用添加命名空间前缀:

    #subapp-container.ant-btn { /* 覆盖Ant Design样式 */ }
  1. 「全局变量冲突」

    // 子应用卸载时清理全局变量
    exportasyncfunctionunmount() {
      deletewindow.__VUE_APP_SHARED_DATA__;
    }
  2. 「通信方案」

    // 使用qiankun全局状态
    import { initGlobalState } from'qiankun';
    
    const actions = initGlobalState({ user: null });
    
    // 子应用监听变化
    actions.onGlobalStateChange((state, prevState) => {
      console.log('全局状态变更:', state);
    });

作者:掌砣的红旗手

https://juejin.cn/post/7468128714108256265