在微前端这个概念出现之前,我们或多或少都能够联想到另一个词性上有些相似的概念微服务,它从出现后便一直都很火热,并不断催生着后端架构体系的演进,而此刻我们如果细品一下这微字头的两兄弟,探究他们的诞生原因,会有很多有意思的点。
我们暂且忘掉这兄弟两,到一个更高的角度会发现,很多新概念的诞生,一定能够顺藤摸瓜的追溯到一个某个原点上,这个原点我们可以把它比作宇宙诞生的奇点,压抑了足够久之后发生了大爆炸,然后逐渐向外扩散生成万物原型,so...... 我们复盘一下处在这个信息高速时代,当网络带宽越来越大,覆盖范围越来越广,延迟越来越低,触达人群越来越多的时候,让web从1.0进化2.0、3.0、4.0,在这个全民入网的时代,网络条件如此优越的时代,是什么开始按捺不住那庞大的能量,开始肆无忌惮的爆炸起来了呢?
聪明的你一定意识到了,它就是数据,优越的网络条件如同肥沃的土壤,让全民皆参与到内容生产当中,于是乎海量的数据疯狂生长,让我们进入了大数据时代,面对如此磅礴的体量,云计算、分布式等新概念蜂拥而出,有了用武之地,或者说数据的爆炸倒逼着这些技术的产生与发展。
再回到微服务与微前端两兄弟上,想一下是不是同样的也正因为数据的爆炸,需要精细化的运营各种场景,让我们传统的单体服务架构上不得不承载着越来越多的业务呢?
这种包含n个服务构成的一个后台项目,从开发侧看,只能在既有的技术栈上不停的叠加新功能,当新的技术福利诞生时想作替换将是一场噩梦,从运维测看,因整个项目打包在一起构成了一个产物,而不得不面对任何一点修改都必须全部一起发布的繁琐。
于是乎微服务诞生了,将这种巨石服务或按各职责、或按功能、或按业务等维度作为拆分的维度,拆解成一个个服务,可独立构建、独立部署,通过rpc
通信做到应用的语言架构无关,让开发者不再捆绑到某一个编程语言上,当然了,作为拆的代价,服务注册、服务治理、服务发现、服务熔断、服务监控、链路追踪等新机制的引入带来了更高的复杂度,所以服务的拆分一定是一个渐进式的过程,为了拆而拆必然带来运维上的更高成本与挑战。
和微服务不一样的是,如果我们不死磕微前端概念,而是从拆字这个角度来看,其实微前端其实从html诞生那天开始一直都存在,我么仔细回想一下,传统的服务器端渲染,是不是一个路由对应着服务器端一个html页面的字符串返回呢,这一个返回对应着后端的一个页面模板,不管它是静态生成的还是动态生成的,它在后端的目录结构里都是一个个独立存在的文件,所以我们一直都是在用微前端搭建项目的视图部分。
随着前端的业务复杂度极巨爬升,传统的服务端渲染方式给前端工程化带来了巨大的挑战,因为工程化的基础是组件化,而后端的模板引擎所支持的组件化天生有着用绵软无力的感觉,无论是书写体验还是调试体验都远远弱于浏览器端渲染,而随着作为浏览器端主战场的不二人选js,生态周边越来越丰富与强大,如node的诞生为前端工程化催生了大量优秀的构建工具、编译工具,js引擎的性能也一直在攀升,js自身的规范和语言特性也不断的进化,以及和相关组件化开发的优秀库如春笋版冒出(react、vue、angular、svelte...),良好的npm生态让库共享易如反掌等,在这种有利于前端工程化的土壤培育下,让我们面对复杂的前端项目时,如可忽略seo优化,首屏速度等因素时(尽管也有类似的解决方案),倾向于从服务器渲染迁移到浏览器端渲染了。
前端工程化也面临着后端同样的问题,一个工程随着时间流逝会逐渐堆叠大量业务,从而让一个前端项目慢慢演变成一个巨石应用,基于此背景下在micro-frontends上对微前端做了如下阐述:
微前端背后的想法是将网站或 Web 应用程序视为由独立团队拥有 的 功能的组合。每个团队都有不同的业务领域或任务,它关心和专注于,一个团队是跨职能的,从数据库到用户界面,端到端地开发其功能,但这个想法并不新鲜,它与自包含系统的概念有很多共同之处。在过去,这种方法被称为垂直系统的前端集成。但微前端显然是一个更友好、更简洁的术语。
并进一步提炼出以下5点微前端的核心思想
以上阶段1里强调的5点特性看起来似乎给微前端下了一个相当完美的定义,以至于后来的各种微前端框架都在这5个核心思想指导下去做实现,直到2020年 webpack 5 module federation
(以下我们简称MF)模块联邦诞生,并对此特性在官网做了一个很简单的介绍:
模块联合的动机,让多个单独的构建应该可组合为一个应用程序,这些单独的构建之间不应该有依赖关系,因此它们可以单独开发和部署,这通常被称为微前端,但不限于此。
细细玩味这段话,我们发现webpack 5视角下的微前端仅需要包含3个特点:独立开发、独立部署、运行时组合。
如果你基于webpack 5 MF
发布过远程模块,你会知道它并不包含micro-frontends站点里提到的隔离团队代码这个关键点,尽管我们知道涉及到代码运行隔离需要用上shadowrealm(未来的隔离方案)、proxy window、iframe 等方案,但MF并未强调这一点,所以看起来MF理解下的微前端是阉割版的微前端?
我们将micro-frontends 和 webpack 5 两个出处的微前端定义做一个对比,并提出一个灵魂的拷问,是否以下表达成立?
事实上随着模块联邦这个概念开始逐渐深入人心,微前端架构已经分裂出两个方向:
容器型微前端
我们把以single-spa为代表的这一类方案统称为微容器,在single-spa走红之后市面很多基于single-spa二次封装的库如雨后春笋般涌出,典型的代表作如阿里的qiankun,意在解决一些single-spa未解决的问题并让其更适合企业级开发,同时也诞生了很多非singlespa系的框架,如京东的micro-app、腾讯的wujie等,它们的细节实现各有差异,包含js沙箱隔离、css隔离、iframe编排、启用web-component、window代理、接入过程等各个地方的细节也各有千秋,但它们都一个很显著的特点,对应的模块粒度是整个应用,做出的产品可以理解为一种以宏观态的方式来组合多个应用交付给用户使用。
试想一下,你不会极端到以运行时隔离的方式去渲染多个按钮吧?
模块型微前端
相较于微容器宏观态的组合应用方式,微模块则可以形容为微观态的组合方式,它的粒度更小,小到可以是一个函数,一个基础的组件,对于开发者来说,引入微模块和引入一个普通的js包没有任何区别,他们在使用上也并无任何区别,但恰恰是这一点!是它和微容器最大差异之一,微模块的使用方式回归到了js语法本身。
微容器和微模块在开源社区均有很多实现,它们的特点很明显
所以基于两种方案搭建的微前端架构也是区别非常明显的
我们只要深刻理解到微模块是天然就假定运行在同一个宿主里(即同个一js环境里),它要解决的核心问题是大规模独立构建的应用间如何快速动态共享公共模块这个棘手问题。
例如你有100个内部的前端项目依赖了lodash-1.0.0,突然该库暴露了一个漏洞,你需要100个前端项目全部重新构建升级到1.0.1才代表安全解决此漏洞问题,而基于模块联邦的lodash,你仅需要构建一次mf-lodash,其他项目即可引用到最新的安全代码。
再综合考虑到两个以下关键点,就很容易得出技术如何选型的结论了:
1 是否需要多技术栈混合开发(react、vue...)
2 是否需要多版本技术栈同时迭代等(vue2, vue3...)
因为微模块是微观态的组合方式,它可以迅速的将你逐渐庞大的应用拆为一个个可独立部署的组件并再次组合起来,相对于微容器方案,大多数时候或许你的新项目并不需要介入微容器。
当你需要组合一些第三方应用或自己的其他技术栈应用,并需要让它的就是被隔离起来安全的运行时,微容器是你武器库里适合拿出来的强力武器。
事实上它们搭配起来混合使用将是相辅相成的完美组合,你可以先使用微容器再接入微模块做跨应用模块动态共享,或先使用微模块再套上微容器做运行时隔离,取决于你的项目发展到了什么阶段。
实际上,我们在这方面已经做了大量的工程实践,微容器推出了无界,微模块推出了hel-micro
这里给出一个混合架构的例子可供大家参考:wujie和hel-mico搭建微前端,大家可以将此思路复制到其他社区方案,例如(micro-app + mf)(qiankun + hel-micro)等。
例子里有无界容器里加载远程react组件的示例
有无界容器里加载远程lodash模块的示例
hel-micro是业内里首个模块联邦 sdk 化,免构建、热更新、工具链无关的微模块方案,让模块联邦技术从构建工具插件层面提升到 sdk 层面,使用更灵活,模块流通性更好(工具链无关)。
无界微前端是一款基于 Web Components + iframe 微前端框架,具备成本低、速度快、原生隔离、功能强等一系列优点。
它们均已在腾讯云、腾讯新闻的多个toB、toC场景经历考验,期待你的关注与了解,让我们一起在微前端领域探索出更多的可能性。
本文讨论了很多关于微前端的新理解,你理解到究极奥义了么,是不是和你和你之前所接触的微前端概念有所不同呢,期望你能在 容器型微前端 和 模块型微前端 之间找到最佳的平衡点并付诸实践。