📖阅读本文,你将
- 可能什么也学不到。(毕竟本文以经验分享为主,代码偏少)
- 了解一个
vue3
巨石项目落地为 react
容器 + vue3
子应用微应用方案的落地全过程。
你问我为啥要切换到
React
技术栈?公司决定统一技术路线,选了 React
,冒办法啊...
- 了解一些已经实际落地的工程化方案;
一、背景
复杂的方案。是为了解决复杂的问题。
假设某产品具备 “Sass
化”、“多业态支持”的模式。
在此模式指导下,可以设想的使用场景为:在同一套后台管理平台中,不仅可以按需给用户分配可用模块、还必须支持同一模块在不同业态下的“区分性”和“定制性”。如图:
上述描述的方案,如果继续采用 SPA
显然已经不在合适,需要将前端项目进行一些改造,以适配最新的业务形态。
SPA
不适合的原因:
- 业务代码杂糅在一个仓库内,多业态/纯订制需求会导致代码仓库异常臃肿,难以维护;
- 有任何模块更新都会导致项目全量更新,可能导致无关业务;
所以我们需要前端项目具备一种新的形态:
- 业态与业态、项目与项目之间独立构建、发布,可增量部署;
- 业态与业态、项目与项目之间的差异性代码独立管理;
- 公共代码依然可以复用;
因此,经过讨论论证,我们选择了 “微前端” 方案来进行前端调整。
二、微前端是什么?
关于微前端,qiankun.js
官网有一段非常清晰的描述。 qiankun.umijs.org/zh/guide
Techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently. -- Micro Frontends
微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。
微前端架构具备以下几个核心价值:
- 技术栈无关
主框架不限制接入应用的技术栈,微应用具备完全自主权 - 独立开发、独立部署
微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新 - 增量升级
在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略 - 独立运行时
每个微应用之间状态隔离,运行时状态不共享
三、技术选型
决定采用微前端架构后,我做了如下技术选型的对比:
- single-spa(11.2k star)
仓库地址:github.com/single-spa/… 微前端实践的先行者,它定义了一套微前端生命周期,并提供了维护整套声明周期的方法。 - qiankun.js(12.6K star)
官网:qiankun.umijs.org/ 阿里巴巴开源框架,在 single-spa
框架的基础上进行了二次封装,让使用变得更加容易。 - mirco-app(2.6K star)
官网:micro-zoe.github.io/micro-app/ 来自京东零售团队的开源框架,基于类WebComponent进行渲染。 - webpack5 Module Federation
文档地址:webpack.docschina.org/concepts/mo… webpack 5.0 推出的重大更新之一,它提供了一套应用间互相依赖的加载规范。
通过权衡,最终选择了 qiankun.js
作为微前端框架。
理由如下:
-
single-spa
本身做的事情太少,需要从 0 - 1地做太多工作。 -
micro-app
的社区热度相对较低,踩坑可能性较大。 - webpack 5 模块联邦方案需要构建的每个微应用都以模块联邦形式构建,存量代码成本较高;
- 公司推行的
Antd
脚手架采用umijs
构建项目,与qiankun.js
之间生态吻合度较高; -
qiankun.js
使用者较多,且社区活跃,封装程度较高,适合在没有太多深度订制的情况话采用;
四、容器与微应用拆分
在 qiankun.js
的框架中,有两个基础概念:“容器” 与 “微应用”;
- 容器:指加载网页后会首先加载和渲染的内容,负责后续管理微应用声明周期、全局状态 等工作。
- 微应用:指具备独立生命周期、独立状态、独立构建的子应用;
微前端的基本结构如上图所示。(实际情况可能比这要复杂,因为微应用与微应用之间也是可以互相引用的)
因此,在正式开始编码之前,我们从 "功能"、"业务"、"实现" 等多个方面进行考量后按如下粒度进行业务拆分:
- 容器具备以下能力:
- 微应用管理
- 菜单、导航 等管理
- 全局用户状态管理
- 全局请求拦截
- 修改密码、消息中心、样式选择 等全局功能
- 微应用则拆分为四大类:
- 超管微应用
- 基础应用
2.1 登录
2.2 系统管理
3. 核心业务应用
3.1 核心业务1
3.2 核心业务2
...
4. 非核心业务应用
五、在 umi
脚手架上使用 @umi/plugin-qiankun
本节为具体迁移步骤:
5.1 按官网文档进行umi构建时配置
.umirc.ts
5.2 在 app.tsx
导出 qiankun
选项
app.tsx
5.3 微应用调整(以 system
子应用为例)
- 修改
package.json
属性name
为system
; - 修改
webpack
配置,增加如下内容,使构建为umd
格式:
- 修改
webpack
的publicPath
为: '/micro-apps/system/' - 修改项目的
route
模式为history
模式(vue项目);其根路径修改为:/system
5.4 给子应用增加生命周期
main.js
代码略;
5.7 子应用使用容器的 axios
实例
其实思路很简单,容器初始化 axios
实例后,挂载到 window.SDK.request
上。
然后在子应用 src/utils/request.js
里定义:
这样就能让子应用的请求被统一拦截处理了。
5.8 其他逻辑细节
由于迁移逻辑细节太多,不在此赘述,大概陈列一下所做的工作:
- 登录状态、token 管理
- 菜单、路由、权限等管理
- 全局用户信息管理
- 全局权限状态提供
- 项目
vuex
状态管理拆分 - 页面拆分
- 等等...
六、通过代理层获取资源
qiankun.js
官方用例使用端口区分 微应用
和 容器
,在实际开发中这显然开销过大。
开发期可能需要起一堆应用🤣。(如图)
当然,不必惊慌,方法总比困难多。
当你需要 “容器” 或 “其他微应用” 的资源时,完全不必局限在本地端口中获取,也可以在某个开发环境上直接拉取。
此时,一个独立轻量的 “代理层” 可能就会显得 恰如其分。
虽然也可以在每个微应用内设置代理,但是很显然,一个轻量的代理层会让整个结构变得更加便捷,灵活。
为此,我专门写了另一篇实践文章:《充分且简单!打造专属“轻量代理神器”》,地址:juejin.cn/post/709406…
七、“微应用拆分” 与 “代码共用”
在 SPA
项目里,因为所有代码都在一个项目里,代码共用是一件简单且自然的事情。
但是一旦把业务拆分成 N
个微应用之后,就变得复杂起来。
如果把所有公共代码打包成 npm
组件显然是无法接受的,因为成本太高。
因此我们采取了如下方案:
yarn workspace + git submodule
- 新建一个名为
smart-vue3
的仓库,其package.json
中name
命名为@chunge/smart-vue3
- 在业务仓库内执行:
这样就可以创造一个软连接指向目标仓库。
- 在业务仓库内的
pacage.json
里添加属性:
- 执行
yarn
这样就可以在 node_modules
里创造一个名为 @chunge/smart-vue3
的项目。
通过这种方式,我们可以将需要复用的代码存放其中,完成低成本的“跨应用代码复用”;
通过以下方式,可轻松引入复用代码:
代码略;
八、使用 webpack externals
特性进行微应用瘦身
本次拆分中,因为几乎所有子应用都是用了 vue
/element-plus
/echarts
等依赖。所以子应用大量的体积是可以优化的。
在容器的 src/pages/document.ejs
文件中,增加以下标签到 head
中:
然后在子应用的 webpack
配置中增加如下内容:
这样可以达到让每个微应用的体积降低 1.2M
左右;
九、构建脚本
微前端的构建通常有两种思路:
- 直接构建
这种思路是在一次构件中,将所有需要的微应用依次构建,然后按需要组织构建物,最终形成一个前端制品提供给用户。
- 先构建制品,再组装制品
这种方式是分别给每个容器、微应用设置版本号,打包成前端制品。在按需拉取打包后的制品,按需组装成最终的前端制品。
方案2显然是更适合大型项目的管理方法,也可以达到“制品级”复用的效果的。
但也有很多管理上成本的开销,一般在项目较为成熟时使用。
在产品成熟之前,采用方案1开销更小,少造轮子。
具体实施方案如下:
- 新增一个
builder
项目。 -
builder
通过git submoudle
关联到所有微应用和容器。 - 通过脚本依次构建
- 通过脚本搬运构建物,组成制品。
代码略;
十、展望
“背景”介绍了本项目“微前端”化的背景,是 SASS
化思路下的产物,也是巨石应用的救星。
展望:在企业信息化、服务化发展进程中,“微前端”方案具备非常大的竞争力与优势,“制品级复用”、“巨石项目拆分” 等都是提效和降低风险的有效举措。