cover_image

基于qiankun的微前端渐进式升级

张俊锋 吕明 王慧 网易传媒技术团队
2023年03月13日 03:55

摘要

通过引入qiankun微前端框架,实现项目的渐进式升级,既能快速引入新的框架开发新的功能,又能灵活的根据需求逐步翻新已有的功能,直至整个项目完成重构升级。

一、项目背景
原项目是6年前开始做的项目,现有70+的功能模块,并一直有新需求在迭代开发。旧的架构基于Gulp+AngularJs+Bootstrap来实现,已经无法满足现有的开发需求。
随着长期的迭代,原框架存在以下几方面的痛点问题:
  • 技术框架老旧,依赖也较多较老,且很多已经停止维护;

  • 功能耦合,代码逻辑复杂,一个小的改动,可能影响很多功能;
  • 代码冗余,存在着许多冗余代码,组件化开发困难,代码复用困难;
  • 新需求不断,开发效率越来越低,开发体验较差。

基于以上问题需要进行前端架构重构来解决,同时重构可能会遇到的问题:
  • 项目功能较多,全量升级时间较长;
  • 新需求不断,边重构边迭代,会有重复开发问题;
  • 回归测试成本高,风险高;
  • 团队开发人力有限。

鉴于项目现状和众多问题,我们的目标是,构建一套既能够满足产品不断迭代,又能够进行重构升级,提升开发效率的重构方案;由此,选用了微前端的方式对项目进行渐进式升级。
 
二、项目思路和方案

2.1技术选型

  微前端方案对比

图片

  • iframe,这个是大家比较熟悉的,甚至在微前端概念出现之前大家就在用的方案, iframe不是单页应用;

  • 还有一类是自己实现了微前端功能的框架,比如比较成熟的single-spa,有的基于single-s
    ‍‍‍‍‍
    pa,或者类似于single-spa的,我统一把他们叫作
    类single-spa方案;
  • Webpack5 Module Federation 可以把多个无单独依赖的、单独部署的应用合为一个,或者可以更细粒度的,打包一个小的微模块,来实现更细粒度的微前端;
  • ESM是ES Module的缩写
    ,是ECMAScript 2015中提出的一种前端模块化手段;
  • Web Components是由google推出的包括浏览器原生组件Custom Element、Shadow DOM、HTML templates三种技术规范。

我们调研和尝试了各种微前端的实现方案,最终选择了开源的qiankun框架,主要是有以下几点原因:
  • qiankun是基于single-spa的,原项目是AngularJs的框架,single-spa提供了一套single-spa-angularjs的中
    间件;
  • qiankun基于import-html-entry实现了HTML Entry,可以直接将原项目的HTML文件作为子应用的接入入口;
  • qiankun实现了single-spa没有实现的js沙箱和css隔离。
基于上面的特点,可以不用对原项目做过多的修改,低入侵的接入微前端。

2.2技术架构图

图片

我们将应用拆分成主应用、子应用两个部分,左边橘色部分是采用了新框架的主应用,通过qiankun注册了子应用,右侧为原项目作为子应用,通过引入single-spa-angularjs的中间库,向主应用导出了生命周期钩子方法bootstrap, mount,unmount。权限、布局、菜单等公共部分在主应用内实现,还有新功能也在主应用内实现,子应用共享主应用的权限、布局、菜单等公共部分。
通过路由分发,新功能和已重构的功能页面直接走主应用的路由,对所有未重构的功能页面路由地址统一增加路由前缀,主应用通过路由前缀匹配,然后调用子应用的生命周期方法,访问到子应用(也就是原项目)的路由,再进到原项目的功能页面;
通过这样的架构就实现了新旧项目并存,渐进式重构,而且主应用和子应用相互独立,都可以独立开发、部署。

2.3接入步骤

主应用(Angular11)
  • 注册子应用,引入子应用生命周期
  • 获取菜单权限,将未重构的页面统一增加/ng路由前缀
  • 通过路由前缀(/ng)来调用挂载子应用

图片
子应用 AngularJs

  • 引入single-spa-angularjs中间件库,导出相应的生命周期钩子
  • window.__POWERED_BY_QIANKUN__判断是否是为qiankun子应用,设置路由前缀,控制菜单、水印等渲染,非登录状态跳转主应用登录页

图片
图片

2.4问题解决

菜单共享问题
因为是两套框架,对应着两套路由,要避免之间的冲突,保证功能都能够正确的渲染和相互的跳转,可以实现细化到每个子菜单页面都可以渐进式升级。通过主应用来管理和渲染菜单,主菜单和侧边菜单都在主应用内,子应用共享主应用的菜单。
主应用请求接口拿到用户的权限菜单列表后,每个菜单地址需根据功能页面的不同情况做不同的处理。如图所示,功能页面分为三种情况:
图片
  • 新增功能页面直接在主应用内实现,可以直接通过主应用的路由直接访问到不用做处理;
  • 已重构的功能页面,通过一个UrlMap的来配置化管理,转换成已重构的主应用的路由地址,然后主应用访问到已重构的功能页面;
  • 将未重构的页面统一增加/subApp前缀,当点击URL前缀为/subApp时,qiankun会将子应用挂载到指定ID为subapp-viewport的container 中,同时依次调用子应用暴露出的生命周期钩子;在子应用被挂载的时候,可以通过window.__POWERED_BY_QIANKUN__来判断,不进行渲染原项目的导航,并统一给路由增加/subApp的前缀,这样在子应用的路由就可以加载到为重构的子应用页面。通过qiankun提供的应用间通信方法,主应用通知子应用当前要访问的子应用路由地址,来保证子应用页面能够正确加载。


图片
图片
CSS样式隔离问题
qiankun的css沙箱默认只能确保单实例场景子应用之间的样式隔离, 开启严格 { strictStyleIsolation: true } 的样式隔离模式又需要对项目做较多的更改。
我们充分利用组件化开发的优势,避免使用全局样式,主应用内功能页面样式和子应用的样式都是动态以style标签挂载到document上的,只有主应用的Layout布局组件有可能污染子应用的样式,只要规范Layout部分的css,就可以实现主子应用的样式隔离。
 
子应用静态资源加载问题
微前端主应用要访问到子应用的静态资源,qiankun是通过fetch的方式请求加载,需要把子应用的静态资源全部改成完整的包含域名的地址,并且要支持跨越请求。考虑到我们的原项目是Gulp构建的AngularJs项目,需要做大量的改动,而且会破坏原项目的独立的运行。
因此我们开发和线上环境,都是通过配置代理的方式来实现的,避免了对原项目的大量改动。
 

三、项目影响力与产出价值
3.1项目价值
方案很好的解决原项目的痛点和重构的问题,并具备以下特点:
  • 增量升级,快速接入新框架,开发新的功能,避免全量重构重复开发问题;
  • 渐进式升级,可根据产品需求,灵活地对经常的迭代的功能模块进行升级重构;
  • 低风险, 每次上线都是根据需求上线部分功能,降低了重构上线的风险;
  • 低入侵,原项目仍可独立运行、开发、部署。

qiankun微前端渐进式升级实践方案,探索了一种高可用、高通用、跨技术栈的渐进式重构方案,可以推广到其他的大型项目的重构中,快速接入微前端,实现渐进式升级。
3.2通用性
我们遇到的项目重构问题是大型前端项目重构普遍存在的问题,微前端方案本身就有跨技术栈的特点,适用于前端各种技术栈的重构,同时qiankun基于 single-spa封装,提供了更加开箱即用的 API。
该项目中实现的导航共享,权限管理,资源加载、CSS/JS隔离等方法,也是可以复用到其他项目的重构中,比如我们团队内部其他系统也有AngularJs框架,完全可以复用整套方案,实现渐进式升级。
因此,该实践方案具有很好的通用性,主应用框架可以选择vue、react、angular等主流框架,原项目可以是vue/react/angular/angularjs/jquery等,结合我们实现的权限管理、菜单管理、资源加载等通用性方法,满足大部分web应用渐进式升级
 
四、项目未来规划和展望
对该方案进行优化抽象,封装出一套快速通用的方法,解决不同项目接入qiankun普遍会遇到的问题。



继续滑动看下一个
网易传媒技术团队
向上滑动看下一个