📖阅读本文,你将

  • 可能什么也学不到。(毕竟本文以经验分享为主,代码偏少)
  • 了解一个​​vue3​​ 巨石项目落地为 ​​react​​ 容器 + ​​vue3​​ 子应用微应用方案的落地全过程。

你问我为啥要切换到 ​​React​​​ 技术栈?公司决定统一技术路线,选了 ​​React​​,冒办法啊...

  • 了解一些已经实际落地的工程化方案;

一、背景

复杂的方案。是为了解决复杂的问题。

假设某产品具备 “​​Sass​​化”、“多业态支持”的模式。

在此模式指导下,可以设想的使用场景为:在同一套后台管理平台中,不仅可以按需给用户分配可用模块、还必须支持同一模块在不同业态下的“区分性”和“定制性”。如图:

巨石瓦解!我把Vue3巨石应用拆成了12个微应用~_前端

上述描述的方案,如果继续采用 ​​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​​ 的框架中,有两个基础概念:“容器” 与 “微应用”;

  • 容器:指加载网页后会首先加载和渲染的内容,负责后续管理微应用声明周期、全局状态 等工作。
  • 微应用:指具备独立生命周期、独立状态、独立构建的子应用;

巨石瓦解!我把Vue3巨石应用拆成了12个微应用~_前端_02

微前端的基本结构如上图所示。(实际情况可能比这要复杂,因为微应用与微应用之间也是可以互相引用的)

因此,在正式开始编码之前,我们从 "功能"、"业务"、"实现" 等多个方面进行考量后按如下粒度进行业务拆分:

  • 容器具备以下能力:
  1. 微应用管理
  2. 菜单、导航 等管理
  3. 全局用户状态管理
  4. 全局请求拦截
  5. 修改密码、消息中心、样式选择 等全局功能
  • 微应用则拆分为四大类:
  1. 超管微应用
  2. 基础应用

2.1 登录
2.2 系统管理
3. 核心业务应用
3.1 核心业务1
3.2 核心业务2
... 4. 非核心业务应用

五、在 ​​umi​​​ 脚手架上使用 ​​@umi/plugin-qiankun​

本节为具体迁移步骤:

5.1 按官网文档进行umi构建时配置

​.umirc.ts ​

{
...,
mountElementId: 'root', // 指定根元素
qiankun: {
master: {
apps: [] // 无需在配置时增加,为了动态加载,均采用运行时添加微应用

5.2 在 ​​app.tsx​​​ 导出 ​​qiankun​​ 选项

​app.tsx​

const fetchQiankunConfig = async () => {
return {
apps: [
{
name: 'system',
entry: '/micro-apps/system/',
},
{
name: 'login',
entry: '/micro-apps/login/',
props: {
onLoginSuccess: async (
) => {
// 登录后跳转逻辑
},
},
},
],
routes: [
{
path: '/login',
microApp: 'login',
exact: true,
},
{
path: '/',
component: PageWithHeaderAndSider,
routes: [
{
path: '/system',
microApp: 'system',
}
]
}
],
prefetch: false, // 关闭预加载
}
}

export const

5.3 微应用调整(以 ​​system​​ 子应用为例)

  • 修改​​package.json​​​ 属性​​name​​​ 为​​system​​;
  • 修改​​webpack​​​ 配置,增加如下内容,使构建为​​umd​​ 格式:
const pkg = require(resolve('./package'))
{
output: {
library: `${pkg.name}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${pkg.name}`
  • 修改​​webpack​​​ 的​​publicPath​​ 为: '/micro-apps/system/'
  • 修改项目的​​route​​​ 模式为​​history​​​ 模式(vue项目);其根路径修改为:​​/system​
const history = createWebHistory('/system')

createRouter({
history,
...
})

5.4 给子应用增加生命周期

​main.js​

const { bootstrap, mount, unmount } = initMicroApp({
entry: App, // 入口
beforeMount, // 初始化之前的逻辑
beforeUnmount, // 卸载前逻辑
afterMount // 挂载后逻辑
})

export

代码略;

5.7 子应用使用容器的 ​​axios​​ 实例

其实思路很简单,容器初始化 ​​axios​​​ 实例后,挂载到 ​​window.SDK.request​​ 上。

然后在子应用 ​​src/utils/request.js​​ 里定义:

export default request = window.SDK.request

这样就能让子应用的请求被统一拦截处理了。

5.8 其他逻辑细节

由于迁移逻辑细节太多,不在此赘述,大概陈列一下所做的工作:

  • 登录状态、token 管理
  • 菜单、路由、权限等管理
  • 全局用户信息管理
  • 全局权限状态提供
  • 项目​​vuex​​ 状态管理拆分
  • 页面拆分
  • 等等...

六、通过代理层获取资源

​qiankun.js​​​ 官方用例使用端口区分 ​​微应用​​​ 和 ​​容器​​,在实际开发中这显然开销过大。

开发期可能需要起一堆应用🤣。(如图)

巨石瓦解!我把Vue3巨石应用拆成了12个微应用~_Vue.js_03

当然,不必惊慌,方法总比困难多。

当你需要 “容器” 或 “其他微应用” 的资源时,完全不必局限在本地端口中获取,也可以在某个开发环境上直接拉取。

此时,一个独立轻量的 “代理层” 可能就会显得 恰如其分

巨石瓦解!我把Vue3巨石应用拆成了12个微应用~_json_04

虽然也可以在每个微应用内设置代理,但是很显然,一个轻量的代理层会让整个结构变得更加便捷,灵活。

为此,我专门写了另一篇实践文章:《充分且简单!打造专属“轻量代理神器”》,地址:​​juejin.cn/post/709406…​

七、“微应用拆分” 与 “代码共用”

在 ​​SPA​​ 项目里,因为所有代码都在一个项目里,代码共用是一件简单且自然的事情。

但是一旦把业务拆分成 ​​N​​ 个微应用之后,就变得复杂起来。

如果把所有公共代码打包成 ​​npm​​ 组件显然是无法接受的,因为成本太高。

因此我们采取了如下方案:

yarn workspace + git submodule

  1. 新建一个名为​​smart-vue3​​​ 的仓库,其​​package.json​​​ 中​​name​​​ 命名为​​@chunge/smart-vue3​
  2. 在业务仓库内执行:
git submodule add <url> src/smart-vue3

这样就可以创造一个软连接指向目标仓库。

  1. 在业务仓库内的​​pacage.json​​ 里添加属性:
{
"workspaces": [
"src/smart-vue3"
  1. 执行​​yarn​

这样就可以在 ​​node_modules​​​ 里创造一个名为 ​​@chunge/smart-vue3​​ 的项目。

通过这种方式,我们可以将需要复用的代码存放其中,完成低成本的“跨应用代码复用”;

通过以下方式,可轻松引入复用代码:

import { xxx } from '@chunge/smart-vue3/components/table'

代码略;

八、使用 ​​webpack externals​​ 特性进行微应用瘦身

本次拆分中,因为几乎所有子应用都是用了 ​​vue​​​/​​element-plus​​​/​​echarts​​ 等依赖。所以子应用大量的体积是可以优化的。

在容器的 ​​src/pages/document.ejs​​​ 文件中,增加以下标签到 ​​head​​ 中:

<script src="//unpkg.com/vue@3.2.33"></script>
<script src="//unpkg.com/element-plus@1.1.0-beta.24"></script>
<link rel="stylesheet" href="//unpkg.com/element-plus@1.1.0-beta.24/dist/index.css"
<script src="//unpkg.com/echarts@4.2.1/dist/echarts.min.js"></script>

然后在子应用的 ​​webpack​​ 配置中增加如下内容:

chainWebpack(config) {
config.externals([
{
vue: 'Vue',
'element-plus': 'ElementPlus',
echarts: 'echarts'
},
/^(echarts|\$)$/i

这样可以达到让每个微应用的体积降低 ​​1.2M​​ 左右;

九、构建脚本

微前端的构建通常有两种思路:

  1. 直接构建

这种思路是在一次构件中,将所有需要的微应用依次构建,然后按需要组织构建物,最终形成一个前端制品提供给用户。

  1. 先构建制品,再组装制品

这种方式是分别给每个容器、微应用设置版本号,打包成前端制品。在按需拉取打包后的制品,按需组装成最终的前端制品。

方案2显然是更适合大型项目的管理方法,也可以达到“制品级”复用的效果的。

但也有很多管理上成本的开销,一般在项目较为成熟时使用。

在产品成熟之前,采用方案1开销更小,少造轮子。

具体实施方案如下:

  1. 新增一个​​builder​​ 项目。
  2. ​builder​​​ 通过​​git submoudle​​ 关联到所有微应用和容器。
  3. 通过脚本依次构建
  4. 通过脚本搬运构建物,组成制品。

代码略;

十、展望

“背景”介绍了本项目“微前端”化的背景,是 ​​SASS​​ 化思路下的产物,也是巨石应用的救星。

展望:在企业信息化、服务化发展进程中,“微前端”方案具备非常大的竞争力与优势,“制品级复用”、“巨石项目拆分” 等都是提效和降低风险的有效举措。