本文由 简悦 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」 |
「推荐选择 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>「四、部署优化策略」
「独立部署」:每个子应用单独构建,主应用通过 Nginx 配置反向代理
location /vue2 { proxy_pass http://vue2-server; } location /vue3 { proxy_pass http://vue3-server; }「资源预加载」:
start({ prefetch: (app) => app.name !== 'react16-app'// 按需预加载 });「性能监控」:
// 主应用集成监控SDK import { performanceMonitor } from'@monitor/sdk'; performanceMonitor.init({ apps: ['vue2-app', 'vue3-app', 'react16-app'] });
「五、常见问题解决方案」
- 「样式污染」:
使用
scoped样式(Vue)或 CSS Modules主应用添加命名空间前缀:
#subapp-container.ant-btn { /* 覆盖Ant Design样式 */ }
「全局变量冲突」:
// 子应用卸载时清理全局变量 exportasyncfunctionunmount() { deletewindow.__VUE_APP_SHARED_DATA__; }「通信方案」:
// 使用qiankun全局状态 import { initGlobalState } from'qiankun'; const actions = initGlobalState({ user: null }); // 子应用监听变化 actions.onGlobalStateChange((state, prevState) => { console.log('全局状态变更:', state); });
作者:掌砣的红旗手