原文地址:https://juejin.cn/post/7183954579690618936
说到微前端大家可能并不会感到陌生,在平时的项目开发中或多或少有接触过,近几年国内也是出现了一批比较火的微前端开发框架 qiankun、Micro App、EMP 等, 这些框架能够让我们快速的搭建起微前端的开发环境,能够快速的与现有的项目进行融合,同时在面对一些常见的场景问题时也给出了具体的解决方案。
正因如此每当我问身边的同学或者同事什么是微前端时,他们都会讲这些微前端框架官网的概念给我讲一遍,比如独立开发部署、技术栈无关、项目之间彼此隔离等,然后我就会反问这是真正的微前端吗?或者说这是微前端正确的开发模式吗?本系列文章会通过讲解 single-spa[1] 带大家重新认识微前端,以及微前端的正确开发模式。
微前端是一种架构风格,旨在当一个项目需要由多个小而散的微应用组合时,这些微应用可以独立开发、测试和部署,最终聚合成一个产品进行交付。
独立开发部署
独立开发部署的能力在微前端体系中至关重要,每个微应用都应具备有自己的持续交付流水线(包括构建、测试并部署到生产环境)。
增量升级
对于一个项目而言最理想的代码结构应该是模块清晰、易于拓展、便于维护等,但是由于一些各种各样的原因导致项目里总是会存在一些不合理不理想的代码:
如果要对代码进行彻底重构的话,最大的难点在于没有足够的时间和资源能够支持一步到位,在重构的同时我们既要保证旧模块 -> 新模块的平滑过渡,还要保证新功能的持续迭代交付。
所以,为了实施渐进式重构,我们需要一种增量升级的能力,这种增量升级的能力意味着我们能够对产品功能进行低风险的局部替换。
兼容多技术栈
对于技术栈而言我们第一步是先要治理,再是兼容,治理也就是说我们 host 方要尽量要求所有的 guest(微应用) 方保持技术栈的统一,而对于一些已经存在的旧项目可以采取兼容的方式,后期再慢慢改造。
前面讲完了微前端所必备的几个能力,下面将介绍一个优质/完善的微前端还应该具备哪几个能力,这也是 single-spa 作者在官网中重点提出的几个能力,这里极力推荐大家去看一下 singe-spa 的官网,他可以说是我们微前端界的红宝书。
跨应用导入(Cross microfrontend imports)
跨应用导入的一个非常重要的功能就是能够让我们跨应用的去复用一些能力,这些能力可以是方法、组件、数据等,这样能够大大提高开发效率,避免相同功能的组件、方法重复的开发。
比如说下面这个例子,我们需要开发一个大型的购物后台管理系统,这里面就包括用户管理模块、商品模块、订单模块等,这些模块都是由不同的团队进行开发的,对于每个微应用来说他们都是要知道当前登陆用户的权限信息,此时我们就可以让用户管理模块给其他微应用暴露出获取用户权限的方法。
// 在用户管理模块导出获取用户权限的方法
export function userHasAccess(permission) {
return loggedInUser.permissions.some(p => p === permission);
}
其他微应用就能直接通过 import 的方式导入该方法。
import {userHasAccess}from "@mc/perssonCenter/api.js";
const showLinkToInvoiceFeature = userHasAccess('invoicing');
如果我们没有跨应用导入的功能,那就需要每个微应用都要去维护一套获取用户权限的接口,这是非常麻烦的。
应用间的通信(Inter-app communication)
跨应用通信即微前端项目间可以畅通无阻的进行通信,信息传递的方式分为主动通知和被动通知。举例来说,现实场景中,应用之间需要状态同步,如用户登出时,负责用户功能的微前端项目能够主动发起通知至其他微前端模块,各模块响应通知消息做出必要处理。
下面展示的是主动通知方式进行通信:
// 在商品微应用中导出清空用户状态的api,当用户退出登录时调用
export function clearUserStatus() {
fetch('xxxx').then()
}
在用户管理微应用中,一旦用户退出登录就调用商品微应用清空用户状态的方法
import {clearUserStatus}from "@mc/shop/api.js";
// 退出登录时调用
clearUserStatus()
而被动通知一般会采用发布订阅模式实现,所有微应用订阅同一份全局状态,一旦状态发生改变就会通知所有的订阅者,qiankun 当前就是采用这种方式。因此,对于一个组合而成的项目,一个通信链路有时候是必要的。
共享依赖(Shared dependencies)
共享依赖允许所有的应用在运行时能够使用同一份第三方库的代码,他是依靠运行时模块化实现的(systemjs /浏览器 esm 的支持/module federation)。
共享依赖也是当前社区呼声比较高的需求,在 qiankun 的 issue 里就看到过很多:
但是官方也并没有给出答复,后面我们会介绍为什么 qiankun 他很难去做依赖共享。那共享依赖有什么作用呢?
治理各个微应用。
它能够去统一各个微应用的第三方依赖版本,比如说我们我们当前主应用使用的是 react@17,另一个部门开发的项目是基于 react@16,这个时候就需要要求他们进行一个升级,因为官网也强调最新的 react-hooks 特性就要求使用 hooks 时必须要在同一个 react 上下文中[2]。
减少资源请求,提高页面加载速度。
上面我们介绍的微前端的概念都是来自 single-spa ,他可以说是国外非常具有代表性的微前端框架,国内我们就选取一个非常具有代表性的微前端框架 —— qiankun,我们先来看一下 qiankun 它是怎么定义微前端的:
我们看到前三个其实就是我们上面讲的微前端必须具备的三个能力没有问题,最大的差别就是第四个概念,qiankun 要求所有的微应用必须彼此之间相互隔离,运行时状态不共享,这不是和我们前面讲的优质微前端要具备的能力恰好相反吗?qiankun 为什么要这么设计?
这个问题我们可以仔细思考一下 qiankun 诞生时的背景:
面对这些问题,当前采用的最好的方案是开发出来一套能够大大减少接入成本同时又能够避免项目之间的相互影响的微前端框架,这也就有了大量的 css 隔离方案、js 隔离方案等。
我们再来看看 single-spa 他是怎么看待冲突问题的:
single-spa 他明确了在微前端项目中全局样式和局部样式是可以存在的,下面他分别讲了一些开发全局样式和局部样式的方法,比如最常见的局部样式开发方案有 CSS Modules、Styled Components、PostCSS Prefix 等。
通过这个我们基本就能看出国内外对微前端的开发模式是不同的,single-spa 所提倡的是治理,要求所有的微应用都按照一定的开发规范去开发,这也和我们一开始给出的微前端的定义是相照应的——先拆再合,而国内的微前端框架是先合再拆,针对项目中已有的问题提供相应的解决方案,从而大大减小微前端项目的开发成本。所以他们走的方向也是不同的。
本篇文章通过 single-spa 阐述了什么是微前端,以及一个优质的/完善的微前端应该要具备哪些能力,最后介绍了国内外对微前端的一些不同的看法,总的来说国内的微前端框架它的优势是利旧性好, 面对技术栈杂乱无章的,重叠严重,代码质量较低的场景,都有比较好的效果,但是也就是因为利旧性好才衍生出来一系列的问题,比如说如何在隔离的条件下做应用通信、依赖共享、跨应用导入等,也期待后面官方能给出相应的解决方案。
single-spa: https://link.juejin.cn?target=https%3A%2F%2Fsingle-spa.js.org%2Fdocs%2Fmicrofrontends-concept
[2]react 上下文中: https://link.juejin.cn?target=https%3A%2F%2Freactjs.org%2Fwarnings%2Finvalid-hook-call-warning.html
关于前端技术砖家
砖家,brickspert
前蚂蚁集团前端技术专家
开源库 ahooks 作者,10k+ star ⭐️
开源库 antd mobile 前负责人,10k+ star ⭐️
你可以在以下渠道找到我:
公众号:前端技术砖家
B 站:前端技术砖家
知乎:砖家
掘金:前端技术砖家
Github:brickspert