一、项目背景
目前,我们开发维护的项目主要有 6 个,但是分别对应 PC 和 H5 两个端:
如上图所示,我们 6个项目最开始是一个一个进行开发维护的,但是到后期,这几个项目之间有的部分会有业务逻辑不同,UI 基本相似的情况出现。而这几个项目前端维护人员较少, 这个时候就要考虑开发效率问题,我们希望相同部分共用,而不是每次都去项目里面进行复 制粘贴,重写逻辑。我们引入微前端,将相似的部分抽离出来,创建一个仓库,实现下图效果
但是上图也会造成一个问题,就是我们每抽离一个模块,需要单独申请一个仓库去进行维护, 这样子维护起来也很麻烦,我们在这个基础上进行改进,引入 MonoRepo+qiankun,最终实 现下图效果。所有模块放在一个仓库中,每次出现新的模块,我们在这一个仓库下面去创建 项目,只维护一个仓库,但是可以在不同项目之间进行切换。
二、微前端方案+技术选型
微前端架构旨在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队 的增多、变迁,从一个普通应用演变成一个巨石应用后,随之而来的应用维护难,成本越来越大的问题。
三、目前阶段流行的微前端方案
1、single-spa
在官网中被自称是一个元框架,可以实现在一个页面将多个不同的框架整合。很多微前端方案基于此进行二次开发或者是灵感来源,支持 esm。基本原理是:把 render 函数和 mounted 函数等钩子暴露出来,在远端引入后,在合适的时机执行,把组件挂载到 DOM 上。
缺点:如果不是用成型的框架,如 qiankun,从 0 开始搭建,比较繁琐。
优点:技术栈无关,对代码侵入性不强
2、qiankun
基于single-spa 的微前端解决方案,生产可用,阿里出品。
目前在交易平台项目中订单模块投入使用。 具体代码可以参考购小店的订单模块和购小店的使用方式。
3、MicroApp
一种用于构建微前端应用的极简方案,支持 esm(需要关闭沙箱)京东出品。
优点:qiankun 更多的是用路由的方式引入,microApp 更多是组件方式, 官方文档比较全面:开发文档,比 qiankun 强多了。
4、emp
基于 Webpack5 Module Federation 搭建的微前端解决方案,使用起来更优雅,使用远端的组件就好像本地组件一样,但是必须得是 webpack5。
官方对于跨框架调用不是很推荐,但是提供了Vue3 调用Vue2 组件; Vue&React 互相调用。
5、Garfish
包含构建微前端系统时所需要的基本能力,任意前端框架均可使用。接入简单,可轻松将多个前端应用组合成内聚的单个产品。沙箱隔离机制更完善。
6、Npm
npm 的方式相对来说,更加传统一些,如果用 npm 来做微前端的话:可能需要把原来业务,可复用模块抽离出来,在原来项目重新引入。专门维护一个公共模块的库,编译时候引入,导致最终的文件变大。
7、iframe
限制太大。除了特殊场合,不太推荐使用。
8、MonoRepo
严格来说,MonoRepo不算是微前端的解决方案,类似npm方式,但是也可以达到很方便的共享代码的效果。多个相似项目的代码在一个仓库里,把共用的业务逻辑或者组件抽离到单独 的项目中去维护。
四、技术选型
因为所有的 C 端代码都是用的spack,所以排除了微前端中的 webpack模式
不想对项目改动太大,所以也排除了npm模式和 iframe 模式。
想要各个项目共享组件,所以采用monoRepo+qiankun,把多个项目在一个仓库中维护
五、项目实践关键点
1、在项目根目录下面创建 packages 文件夹,用来存放我们各种包,然后在根目录新建 pnpm 的工作区文件 pnpm-workspace.yaml,输入下面代码就可以将包进行关联
2、路由页面
必须保证微应用加载时主应用这个路由页面也加载了。
主应用注册这个路由时给 path 加一个 *,注意:如果这个路由有其他子路由,需要另外 注册一个路由,仍然使用这个组件即可。这里我们根据不同的项目,activeRule 通过主项目传值的方式告诉子项目。
微应用的 activeRule 需要包含主应用的这个路由 path。
3、安装 qiankun,在主应用注册微应用
4、在微应用导出相应生命周期钩子并配置微应用打包工具
至此,MonoRepo+qiankun 项目搭建完成,可以在子项目中进行各自逻辑开发。
六、遇到问题
1、子项目如何知道应该走对应模块的逻辑
将模块属性进行抽象,根据不同模块创建不同实例对象,在子项目mount 阶段根据主项目传的平台标识进行实例创建。
2、子应用跟主应用不在同一个域名下跨域问题 因为不同子应用,申请的不同域名,单独打包部署,qiankun框架实际上是引入子应用打包生成的js文件,会出现跨域问题。开发环境要在子项目里面设置允许跨域。生产环境我们在nginx对应子项目的域名下面设置允许跨域。
3、登录cookie
我们项目里面没有将域名统一,拉取微应用 entry 的请求都是跨域的,所以通过自定义 fetch 的方式,开启 fetch的 cors 模式.
4、CSS隔离
样式的隔离有很多种处理方式,如:BEM、CSS Module、css 前缀、动态加载/卸载样式表、Web Components 自带隔离机制等。
5、JS沙箱
如何确保子应用之间的全局变量不会互相干扰,实现js的隔离。普遍的做法是给全局变量添加前缀,这种方式类似css的BEM,通过约定的方式来避免冲突。这种方式简单,但不是很靠谱。qiankun 内部的实现方式是通过Proxy 来实现的沙箱模式,即在应用的 bootstrap 及 mount 两个生命周期开始之前分别给全局状态打下快照,然后当应用切出/卸载时,将状态回滚至 bootstrap 开始之前的阶段,确保应用对全局状态的污染全部清零
6、数据通信
可以使用官方的initGlobalState方法
我们这个项目应用之间不涉及数据共享,所以没有顶级 store 的概念。模块之间的简单通信可以通过 eventEmitter来实现。
7、子应用缓存问题
子应用文件更新之后,访问的还是之前版本的内容,没有及时更新。 服务器需要给微应用的 index.html 配置一个响应头:Cache-Control no-cache,意思就是每次请求都检查是否更新。在nginx上配置:location = /index.html { add_header Cache-Control no-cache; }