21

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

 3 years ago
source link: https://zhuanlan.zhihu.com/p/394416090
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

关注微信公众号:大转转FE。 一个有趣的前端团队~

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

背景

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

解决方案

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

成果

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

技术细节

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

https://qiankun.umijs.org/zh。

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

https://umijs.org/zh-CN/plugins/plugin-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组件为什么会抛出访问不安全的问题?

问题如图:

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

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

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

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

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

结语

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK