微前端:我们到底在讨论什么呢?
引子
2018年年底的时候,我们刚做完第一期目标系统,PM同学问:未来我们会将HR系统打通,现有系统有可能嵌入嘛?2019年开始做平台从预算到付款一体化建设,这个庞大的业务流程包含8个子业务模块,前后端总研发人数超40,从系统角度而言,是个空前的规模。我们发现,伴随业务的发展,我们需要做些什么了。
微前端诞生
微前端在2016年首次被提出,来源于一篇论文:https://martinfowler.com/articles/micro-frontends.html,而微前端的概念,更多来源于微服务的启发。微服务在2014年由Martin Fowler提出定义(https://www.martinfowler.com/articles/microservices.html):将一个应用拆分为小的进程,进程间采用如http协议这种轻量级方式沟通,围绕业务做拆分构建及部署,可用不同技术栈实现。而微前端的定义为:一种可将独立前端应用组合成更大整体的架构形态,它带来的关键收益是:
- 更小而内聚,代码更可维护
- 可伸缩且隔离的研发团队
- 让渐进式部分重构或迭代变为可能
由以上概念,微前端是以应用为颗粒度,用拆分、组合式手态解决工程问题的架构。这里的两个关键词是,颗粒度与拆分。单从拆分的角度来说,前端领域已经经历了从多页面到单页面模块化、组件化的阶段,解耦与拆分所带来的好处已经不用赘述。而随着前端能力的发展,越来越多巨型应用开始出现,我们发现似乎缺少了更大粒度的解耦方案。让我们回顾一个巨型前端应用的生命周期,来看看都发生了什么。
微前端能做什么
三类典型场景
一个巨型前端应用的诞生过程可能分为两种情况。
第一种场景,一个或多个庞大的团队,瀑布方式并行研发交付。这种模式的团队在伊始就会遇到第一个问题:多人合作中沟通协作带来的效率损耗,当一个闭环团队中的人越多,信息传递成本将以指数增长。对于前端团队来说,遇到的便是一系列需要通过沟通和约定来解决的研发行为的细节:我的代码文件该放到哪,我的分支能不能合并,我的代码修改是否影响另一模块等等。而随着代码和文件的积累增多,开发人员的文件及代码寻址越来越慢,项目启动速度越来越慢,直至每一次发布,打包的速度越来越慢。团队的每一位研发者的时间开始为与自己所关心内容并不相干的协同噪声买单。
第二种场景是一个项目或许没有庞大的团队,但是经历了漫长的敏捷迭代生命周期,直至有一天成为一个笨拙的庞然大物,面临技术老旧、运维成本高,难以维护的瓶颈。此时团队面临的问题如一颗烫手山芋。这个颗山芋需要重构乃至重写了,然而它的庞大让人望而生畏,很难有一个团队可以再花费以数年为计的时间去重新搭一个新的庞然大物,业务不会停,团队也要开锅。此时渐进部分重构似乎已经是唯一选择了,老服务继续使用,分批重构功能,嵌入老系统,直至最终完全焕然一新。
第三种场景,是拆合能力所带来的天然福利。当业务能力趋近中台化、抑或应用能力插拔化诉求来临时,可插拔的特性让一个微前端应用的能力能够低成本在任一所需应用内卸载、安装、复用。
微前端要做什么
以上三种场景将我们引向了答案:应用级别的拆分隔离。下一个问题是,如何拆分,让我们再看一看拆分的目的。首先架构是为研发者负责的,我们的目的是让研发行为边界清晰,降低协同噪声。这个角度来看,拆分的逻辑取决于研发行为边界,而对于研发行为边界,取决于业务迭代的边界。让一次迭代行为尽可能聚焦在一个更小的团队,代码更满足高内聚低耦合,进行更少范围的发布行为,便达成了目的。
然而与后端微服务概念不同的是,微前端在展示形态上,应该对用户无感知的呈现在浏览器中,这便意味着组合是微前端天然的使命。关于在何时组合,我觉得是一个有意思的问题,对于交到运行过程来说,研发时、打包时、运行时,都有机会和手段做组合,该选择何时呢?抛开何时组合才算微前端不谈,我认为,既然拆分是为了面向研发者的解耦隔离,那么组合的时机越晚,收益便越大。那么,微前端架构在拆合过程中的责任便成了,在研发过程中为拆分隔离提供手段,为运行时组合提供手段,并在运行时统一解决因研发过程中的隔离而带来的副作用。
一切已经变得清晰。
何为好的微前端框架
绕不开的iframe
在18年前后我们讨论微前端的时候,经常会被问到一个问题:这玩意比iframe好在哪?其实回顾上面对微前端的定义,我们大可不必把微前端与iframe对立起来,因为iframe同样可以达到研发过程隔离,运行时合并的目的,而且成本极小。所以我们的问题应该换成,使用iframe来搭建微前端架构,是一种好的选择吗?
架构诉求
架构应该对研发负责,对用户无感。对研发负责包含完成上面说的拆合、解决副作用,此外还隐含着一个尽量降低因架构而带来的成本的诉求。对用户无感包含着:功能、视觉无差,以及隐含着性能上的不降级。
我们先来看看拆合、副作用的事情。
拆合
拆可能是最简单的一步了,确定好边界及维度,用何种手段拆其实并不重要。合的过程也好理解,首先在运行时去做组合,需要一个master去管理各微前端的生命周期及相互之间的串联顺序。
副作用
最后因为研发隔离而带来的副作用,反倒是最值得关注的。让我们看看研发隔离后我们省去了什么:
- 不用管别人是什么技术栈
- 不用管css、js是否与别人冲突
- 不会和别人的代码产生强引用关系
当然,为我们省去的可能不止这三点。然而这三点便是对运行时带来副作用的来源:
- 组合时需要解决技术栈兼容及依赖重复、冲突
- 需要管理js css 冲突
- 需要有微前端与外部交互的能力
架构附加成本
我们再来看看如何尽量降低架构带来的成本。我们关心的无非是研发过程、部署运维两个时段。研发过程,0-1项目框架引入成本、以及已有项目微前端改造,两种场景我们都会经常遇到,前者的成本保证主要是依靠sdk的简洁、易用性。后者则还需要考虑到对不可预知的技术、项目形态的包容度,如,原项目为已js为入口还是html,原项目为前端还是后端渲染。在部署运维时,由拆分而带来的项目数增加,必然同样会带来运维难度的增加。
用户无感
最后是用户无感。功能、视觉无差好理解,就是字面意思。性能不降级,则意味着,新架构后资源加载量、对浏览器的资源占用量不明显增加。
到这里我们已经发现了,微前端同样是一个无法避免缺陷的架构。在部署运维成本、用户性能的角度来看,我们只能投入额外精力尽量做到趋近不降级。所以,你的项目究竟需不需要微前端呢,此时已经有了答案。
这时,我们回过头来看,使用iframe来搭建微前端架构,是一种好的选择吗?从拆、合的角度来看,iframe几乎是完美的,进程级别的隔离、极低的使用成本,跨iframe的通信手段。然而同样因为它的完美带来了用户感知上的不完美:如dom的隔离让弹框弹不到中间;进程的隔离让资源重复加载、内存占用增多;整个应用从由一个url决定状态变成多个等等,并且iframe的设计让研发者没有任何空间可以做弥补。这才是如今市面上的微前端方案均抛弃iframe的原因。
一个优秀微前端框架应该具备的能力
经过上面的分析我们几乎已经得出了微前端架构的需求列表:
拆合
- 管理微前端生命周期
- 串联各微前端间顺序性
处理运行时副作用:
- 处理css、js冲突
- 提供跨微前端通信能力
降低框架引入成本:
- sdk简洁易用
- 尽可能少造成代码侵入
- 适配各类技术框架
- 部署方案尽量简洁统一
用户无感:
- dom上下文不隔离
- 微前端共用进程不隔离
- 处理重复依赖加载
推荐阅读
-
学英语,我们到底在学什么呢?(3)元音字母在弱读情况下的发音规则
-
当我们谈论"色图"色彩不足或刺激度不够时,我们究竟在说什么呢?是对眼神不过摄人心魄的质疑,还是对姿势缺乏诱惑力的批评?是指出身材比例(如胸部大小、腰身纤细、腿型修长)的不满,还是觉得曝光程度和遮掩程度不合适?不管是真人照、虚构角色的图片,还是旨在激起性兴趣的作品,都能找到一些讨论点。 但实际上,关于现实生活中的目标,如如何实现经济独立("财务*")、定义个人理想("理想")、以及具体的行动路径(如"出去闯一闯" - 去哪里、如何行动),这些话题往往更难以清晰表述。有时我们会发现,那些看似鼓舞人心的理想、追求和*,可能仅仅是一个口号或标语,尽管它们庞大而深远,但也可能仅聚焦于某几个关键词,至于更深入的内容,却未能充分展开。
-
微前端:我们到底在讨论什么呢?