对巨石应用说不:转转商业微前端qiankun历史项目迁移升级实践_加载

之前和大家分享过微前端的相关知识(具体可见之前的文章:闲庭信步聊前端 - 见微知著微前端),本次分享qiankun在转转商业的一些实践,及问题解答

背景

商业Crm开发多年,随着开发成长为巨石应用。打包慢、升级难、排查问题链路长,同时性能差,用户体验欠缺。加上产品对系统定位思考不合理,常年的堆积功能,系统混乱。对系统的改造迫在眉睫。

解决方案

通过利用qiankun微前端框架拆分商业Crm,在主应用中不断拆出功能,部署在子应用上,Crm作为主应用(系统基座),加载子应用。在迁移功能到子应用的过程中,完成代码规范和技术栈升级。

成果

已经成功拆分了部分页面,多系统运行,做到了用户无感知技术升级,替换了原有页面,打包和加载速度有所提升。最重要的是没有耽误正常的业务开发。

技术细节

引入qiankun框架,主站点使用原生的qiankun包,文档如下:

​​

子应用使用了umi框架,umi提供了乾坤插件,使用方式和原生的qiankun相比使用更简单,同时可以和原生配合使用,文档可见:

​​

qiankun一共有两种加载子应用的方式

1.提前注册,把需要加载的子应用提前写好,包括注入容器,路由匹配规则,子应用入口

import { registerMicroApps, start } from 'qiankun';

registerMicroApps([
{
name: 'react app', // app name registered
entry: '//localhost:7100',
container: '#yourContainer',
activeRule: '/yourActiveRule',
},
{
name: 'vue app',
entry: { scripts: ['//localhost:7100/main.js'] },
container: '#yourContainer2',
activeRule: '/yourActiveRule2',
},
]);

start();

2.第二种方式loadMicroApp,动态加载子应用。这种方式的好处是可以随时随地,按照业务需求加载子应用

方法的属性介绍:


  • name:区分子应用,在拥有多个子应用的时候,name非常重要,用于通信
  • entry:子应用入口,在本地环境和线上环境可能有区别,需要在区分配置。
  • container:  微应用的容器节点的选择器或者 Element 实例
  • 通过initGlobalState,可以实现主应用和子应用的通信。

下面介绍我们在系统中的使用细节

主站点系统配置

安装

"qiankun": "2.4.1",

将所有需要迁移的路由更改指向,指到专属中转页面,在转转商业Crm中的是

component: () => import('@views/Child')

在Child.jsx中部署乾坤的加载逻辑

import { loadMicroApp, initGlobalState } from 'qiankun';
that.microApp = loadMicroApp(
{
name: 'crmtest',
entry: 'https://crm.xxxxxxx.com/childfirst',
container: this.containerRef.current,
props: { authList: this.props.authList }
},
{
getPublicPath: (v) => {
return '//s1.xxxxxxx.com/biz/bizcrmweb_child_first/static/js/';
}
}
);

商业Crm采用的方式是loadMicroApp,这样的好处是非常灵活,加载哪个微应用可以动态配置,可以随时替换。

子应用系统配置

子应用使用了umi,所以直接使用

"@umijs/plugin-qiankun": "2.22.0",

这是umi提供的qiankun插件,使用相比原生的qiankun要更简单。安装插件以后,在umi指定的config文件夹下面的config.js 开启插件:

qiankun: {
slave: {},
},

然后在入口文件(通常是app.js)上声明生命周期方法,便于主应用调用。注意:不暴露生命周期会导致系统报错。

export const qiankun = {
// 应用加载之前
async bootstrap(props) {
console.log('app1 bootstrap', props)
},
// 应用 render 之前触发
async mount(props) {
console.log('app1 mount', props);
props.onGlobalStateChange((state, prev) => { // state: 变更后的状态; prev 变更前的状态
console.log(state, prev, '子应用收到信息')
})
// props.setGlobalState(state);
},
// 应用卸载之后触发
async unmount(props) {
console.log('app1 unmount', props)
}
}

配置结束后,启动多个项目,将子应用指到主应用规定的域名地址上,就可以开始调试了。

排坑答疑阶段

1.子应用还需要有路由配置吗?如何跳转?

子应用需要有完整的路由配置,主应用将所有的子应用路由都指到了同一个页面(crm是指到了前面提到的child.jsx),并不区分页面,但是路由的是变化的,

如下所示:

[{
path: 'child',
component: () => import('@views/Child'),
}, {
path: 'tools/paper',
component: () => import('@views/Child'),
}, {
path: 'finance/saledetail',
component: () => import('@views/Child')
}]

此时子应用的路由系统会启用,根据当前的路由加载不一样的页面进来。

2.权限系统是如何运行的?

在crm目前的方案中,权限系统是从主应用中注入到子应用中的,每次切换路由,主应用先去加载权限,再加载子应用,保证子应用的权限及时更新。

如下:通过props注入进系统

import { loadMicroApp, initGlobalState } from 'qiankun';
that.microApp = loadMicroApp(
{
name: 'crmtest',
entry: 'https://crm.xxxxxxx.com/childfirst',
container: this.containerRef.current,
props: { authList: this.props.authList }
},
{
getPublicPath: (v) => {
return '//s1.xxxxxxx.com/biz/bizcrmweb_child_first/static/js/';
}
}
);

3.​如何保证样式隔离?

样式隔离是微前端开发中非常重要的问题。

Qiankun在子应用切换的过程中,会自动注销掉上一个应用的样式,这样可以避免微应用之间的干扰。

对于父应用和子应用之间的样式干扰,要分情况对待。

如果是框架的样式干扰,例如商业Crm有技术栈改进的需求(antd3升级到antd4),可以通过给框架样式加前缀,利用webpack处理,自动转换。

对于全局自定义样式有两种处理方法,一种是跟随主应用,子应用就不再配置特别的全局样式。

二是加应用前缀,以及CSS module,也可以解决问题。

4.为什么不开启 scoped css 功能解决样式隔离问题?

对于Vue框架的系统,开启 scoped css 是一个比较省事的解决方案,但是由于React使用合成事件,开启 scoped css 会在页面中开启一个shaow dom容器,这个容器会导致冒泡到document上的事件无法触发,所以不能使用。

不过React官方已经意识到了这个问题,新版本不再全部冒泡的document上。

5.测试环境和线上环境的静态资源配置?

如果公司的静态资源没有部署在系统域名下,可以通过入口处的补充属性getPublicPath指定

{
getPublicPath: (v) => {
return '//s1.xxxxxxx.com/static/js/'; // 静态资源链接
}
}

6.为什么主应用访问子应用资源会提示跨域?

这个问题官方有过回答

由于 qiankun 是通过 fetch 去获取微应用的引入的静态资源的,所以必须要求这些静态资源支持跨域。

如果是自己的脚本,可以通过开发服务端跨域来支持。如果是三方脚本且无法为其添加跨域头,可以将脚本拖到本地,由自己的服务器 serve 来支持跨域。

7.使用umi的qiankun组件为什么会抛出访问不安全的问题?

问题如图:

对巨石应用说不:转转商业微前端qiankun历史项目迁移升级实践_静态资源_02

如果是在https环境下访问http的资源,就会被umi抛出这个问题。这个问题多半出现在 开发阶段,线上部署在同域名下就不会有问题。

解决办法是在node_modules搜索这个错误拦截,删除这段安全判断代码。

或者将两个站点都改成http访问。

8.umi的@umijs/plugin-locale没有提供自定义前缀入参,该如何添加样式前缀?

可以把umi的@umijs/plugin-locale组件拷贝出来,自己添加一个前缀属性,然后当作自定义插件使用。

结语

微前端的概念已经提出来很久了,但是微前端的需要解决的问题还有很多,并不是所有的项目都适合微前端方案。本文分享了微前端的一种实践思路,以及各种具体问题的处理,希望对大家有所帮助。

另外,除了目前比较受欢迎的qiankun以外,webpack5推出的模块联邦也是非常不错的方案,大家也可以尝试一下。

对巨石应用说不:转转商业微前端qiankun历史项目迁移升级实践_商业_03