微前端调研随笔

概述

微前端应该是最近2年前端领域最炙手可热的话题之一,似乎业务领域稍微复杂点不上微前端就没有跟上时代,微前端领域的框架也层出不穷,Webpack都来用Module federation掺和了一把。目前网上的资料更多都在于应用和实现,似乎大家都不关心为什么要用微前端,上就完了。我最近正好在做微前端转型的方案,也关注了微前端3年多了,顺手记录一下我个人对于微前端的理解。

微前端的名字由微服务而来,由知名咨(Hu)询(You)公司Thought Works在2016年提出。但是,微前端和微服务并不一样,微前端在有些人看来也不是一个合适的名字,叫做解耦组合前端(Decoupled composable)似乎更好。微前端大致有两种拆分方式——纵向拆分和横向拆分。纵向拆分根据业务领域将前端整体分解成独立应用,多用于2B领域,也是市面上大多数微前端框架的适用场景,特点是绝大多数情况下,同时只激活一个前端应用。横向拆分更复杂,把一个页面根据业务领域拆分成多个前端组件,多用于2C领域,同时激活多个前端应用。由于我个人对横向拆分的应用领域不是很了解,就不在这大放厥词了。

总体来看,微前端就是把一个大型前端应用根据业务领域进行拆分,解耦各个业务领域,获得开发、部署上的独立性,让业务更快的发展。

解决的问题

首先,思考一个问题,微前端是为了解决开发侧的问题,还是使用侧的问题?回答了这个问题,就大体明白了微前端的适用场景。直接说我个人的结论,微前端基本是为了解决开发侧的问题,对在特定的业务场景下,对于使用侧的体验有一定优化。

微前端最主要解决的问题是开发、部署的独立性要求,尤其在一个项目扩大到需要多个团队协作的时候。多个团队不同的开发节奏和发布节奏会产生严重的互相掣肘,轻则出现各种代码冲突、发布延后,重则出现各种错误、无限期推迟当前版本发布。所以,当2个以上的团队开始在同一个项目上贡献等量工作量时,该考虑是不是要把项目拆分成微前端了。

微前端解决的次要问题是解耦业务领域,其实这也是独立性的另一种体现,但更多是体现在业务逻辑上的,而上面提到的主要体现在工作流程上。解耦是一个软件开发老生常谈的话题,有各种各样的方法论可以用,微前端语境下需要思考的不是怎么解耦,而是解耦到什么程度。不论是纵向拆分还是横向拆分,如果一个微前端应用对外部的数据(状态也是数据)产生依赖,那么这个微前端就事实上丧失了开发、部署的独立性,变成了和其依赖必须同时存在的分离式单体应用。这里并不是说微前端要严格的不对外界产生依赖,而是要想清楚微前端对外面的依赖和对外暴露的功能,也可以理解成微服务的API。

一定有人会反驳,微前端怎么不能提升用户体验呢?在一定程度上是可以的,比如可以让微前端的应用共享用户信息,提高前端应用间切换性能,等等。但这些问题也可以通过其他手段解决,微前端并不是一个必须的选项。大多数情况下,微前端并不能有效提升用户体验,能做到用户无感,已经很不错了。所以,面对问题的时候一定要分清主次,这样才能找到更合适(平衡成本和收益)的解决方案。

带来的新问题

有好必有坏,凡事都有trade off,微前端也不例外。在降低开发部署成本的同时,微前端给使用侧带来了新的复杂度。

首当其冲的是微前端治理。我这里用治理,是为了和使用广泛的微服务治理相对应,包括微前端发布、微前端管理、版本控制等一系列配套工具。与微服务不同的是,微服务需要有专门的工具来负责注册与发现,告知其他的微服务自己的存在,并维持自己的可用性。微前端基本由壳应用加载,通过壳应用在浏览器中协作,所以微前端的注册与发现主要集中在壳应用中,这点从各个微前端框架对于子应用的改造,和提供的子应用间通信的功能也能看出来。虽然治理微前端的难度比较低,但是这仍然是最重要的。壳应用需要知道每个微前端对应的资源地址,并且根据发布更新资源地址,还需要有对版本的管理,便于出现问题回滚以及新版本测试,这些又需要Devops流程和工具链的支持。有了这些配套,微前端方案才是一个可以在生产环境中放心使用的方案,而非一个仅仅是从多个URL加载资源的简单组合器。

另一个需要解决的问题是微前端的划分,这与微服务的划分对应,需要找到一个平衡点,既能解耦绝大部分业务领域,又不会增加太多工作量。微服务可以使用DDD的方法论来指导对业务领域的拆分,微前端也可以参考,但并不能直接后端的分析结果,因为前后端面对的业务场景还是有一些区别的。对于纵向拆分的方式,可以看是否会产生数据耦合来进行判断。如果拆分之后依然需要共享一些数据,那么说明这两块业务领域还是应该在同一个微前端子应用中。

虽然微前端的一个推销点是可以将不同技术栈的应用组合在一起,但这其实并不是一个推荐的方案,基本上只在过渡期发挥作用。和微服务几乎完全独立不同,微前端会共享相同的运行环境,并呈现出近似的应用效果,让用户感觉不到是在使用微前端。这导致了如果使用多个不同的技术栈,可能会对运行环境造成影响,还可能造成冲突。市面上的多数微前端框架都提供了js沙箱作为解决这类问题的方案,但沙箱往往会进一步降低性能。所以,在没有技术债的情况下,更好的办法其实是统一各微前端应用的技术栈,包括但不限于基础框架、组件库、底层依赖库等。这时,如果每个应用对这些共享的依赖单独进行打包,就会产生很大的浪费,而且依赖的版本也不好控制,容易出现冲突。所以统一的进行依赖的加载和管理,也是微前端需要解决的一个问题。

另一个问题是测试,尤其是E2E测试。微前端的应用因为是多个应用的组合,但使用起来却像一个应用,这加大了E2E测试的难度。测试范围怎么划分,跨应用的功能怎么测试,不同应用版本怎么测试,都区别于传统单体前端应用。其实测试的问题也是对微前端拆分、设计水平的考验,后面会谈谈我认为的合理的划分方式。

怎么设计

说了这么多,主要都是在讨论微前端解决问题和带来的新问题,是否采用微前端的决定权还是在自己手中,我的看法是根据拆分的原则,看看拆分后的微前端方案是否能解决开发中遇到的主要问题,如果可以,那么选择微前端的利大于弊,否则,针对主要问题寻找其他的解决方案。参考资料中提供了一个决策矩阵,从多个维度更详细的分析,有兴趣的可以看一看。

那么微前端的拆分原则是什么呢?我个人认为需要考虑以下3个方面:

UI

不管愿意不愿意,我们得承认UI是前端开发中最重要的一环,UI之下写的多么花里胡哨,直接面对客户的还是UI,一个UI烂的产品用户一定不愿意用。而微前端与微服务的一个重要区别也在这里,不管怎么拆分,微前端最后都是组合在一起呈现给用户,而且微前端当年也宣称自己可以解决iframe的UI分裂问题,所以UI上的统一对于微前端来说是一个必须项。既然需要统一的UI,那么最好使用统一的UI库,同时为了避免不同的微前端之间出现UI库版本冲突,最好所有微前端使用统一版本,并且共享依赖。这样既达到了目的,还优化了性能。至于UI库怎么引入,根据构建工具找方案就好了,比如webpack既可以用external,也可以用module federation。

数据

对于现代的前端应用来说,UI由数据驱动,除了展示形式和渲染方式,所有的业务逻辑其实都是对数据的操作。用户不能直接看到数据,为了追求独立性和解耦,微前端的数据对外部的依赖越少越好,同时对外部的影响也同样越少越好。这里就可以参考微服务的拆分方式了,根据业务领域,把相关的业务逻辑放到一起,在数据层上形成聚合,在微前端之间进行解耦,保证同一领域的业务逻辑在一个微前端中。

但是因为前端以整体呈现,有时候可能达不到这种分离的理想状态,一些业务逻辑还是需要嵌入到其他的业务领域之中,这时可以考虑将这块需要共享的业务逻辑封装成组件,在不同的地方共享使用。或者使用Module federation,将这块业务逻辑的组件暴露成remote模块,在其他的地方引入。需要注意的是,Module federation同样伴随着治理问题,如果类似的情况增多,那么一定要把remote模块的治理提上日程。

通信

虽然前面提到了,微前端的数据最好做到独立解耦,但微前端之间难免有需要共享数据或者通信的场景。有一些方案在壳应用上提供了数据管理的功能,我个人的看法是一定不要用。一旦跨应用共享数据,那么就造成了事实上两个应用的耦合,两个应用就成了连体婴儿,很快就会无法维护。应用之间的通信可以通过发布订阅事件来解决,应用对外只发布自定义事件,至于谁订阅了这个事件,应用并不关心。同时应用从外面获取数据靠订阅事件,至于这个数据的来源,应用也不关心,只知道自己需要订阅这个数据的事件就好。事件总线可以由壳应用提供,也可以完全独立,将API注入到每个微前端中就好,这样微前端之间的耦合问题就解决了。微前端注册和依赖的事件,可以在编译时通过事件总线的API获得,从而进行管理。

除此之外,由于微前端应用都运行在浏览器平台上,所以也可以使用浏览器提供的数据存储API来共享数据,如Local storage,Session storage等,类似于微服务共享同一个数据存储或redis。由于功能是运行时提供的,所以微前端之间不会造成耦合,只需要对没有获取到数据的情况进行处理即可。

方案选择

市面上的微前端方案很多,主要提供的是纵向拆分的解决方案,在实现方式上有一些不同,使用上反而没什么区别,都是通过URL加载资源,触发应用的生命周期,提供样式、JS隔离、路由劫持、依赖共享等功能,在性能上也没有太大的差别。由于微前端主要解决的开发过程中的问题,所以一开始直接选择微前端方案的可能性不大,也没有必要。在选择方案时,还是从接入成本和配套考虑,通过POC选择符合自己需求的框架,并尝试接入实际的应用看看效果。

Qiankun、Microapp、无界分别是由阿里、京东、腾讯推出的微前端框架,如上面所说,功能上差不多,实现方式有一些差异,这里不做对比了,有兴趣的可以去自己看文档或源代码,写的比我好多了。

单独说一下Webpack 5带来的Module federation,现在的微前端文章基本都会带着它。Module federation拥有超低的接入成本,并且对开发基本没有影响,所以也受到很多人的欢迎。但是,在我看来,Module federation与其说用来做微前端,倒不如说是更适合用来做包共享。相比于其他微前端方案,Module federation缺乏各种隔离能力,缺乏融合多种技术栈的能力(虽然不提倡)。Module federation过于灵活,给应用维度都划分不明白的人很容易快速的堆积屎山。因为Module federation可以划分到更细的粒度,所以需要更强大的治理能力与之配套,而这些往往都是容易被人忽视的部分。所以,在选择微前端方案时,最好谨慎考虑Module federation。

总结

啰嗦了这么多,一个是记录一下我最近的调研成果,另一个就是表达一下自己的看法,做技术的不应该只考虑技术实现,更应该从问题出发,思考问题如何解决,再用技术找到合适的解决方案。当下的市场环境越来越卷,所有公司都在说降本增效,可是连问题都没搞清楚,就盲目的大干快上,又有多大的概率能取得预期的效果呢?适当的抬头看一看,不一定就是要离开技术,反而换个角度,能让技术发挥更大的价值。

参考

microfrontend.dev/
dev.to/this-is-lea…
www.infoxicator.com/what-are-mi…
github.com/Tencent/wuj…
qiankun.umijs.org/
micro-zoe.github.io/micro-app/

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYs41D6z' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片