在一切开始之前
我希望使用者对
wujie
和qiankun
有一些了解,这会让我的文档写得省力一些
这篇文章没有涉及
merak
相关的任何东西,如果理解了微前端的本质,这个库本身没有什么使用难度,
在项目发布之前,总要对项目做一点总结,排出几大特性几大优点,但在merak
这里稍有困难,
我很难找到一个可靠的数据去论证其优势(甚至很难解释为什么没法找),也很难解释为什么没有custom fetch
,没有script
标签的控制器这些其他框架必备的东西
我只能先从一次qiankun
项目的经历说起。这不是一个严谨的项目,我也没有坚持做下去,但其中的一些困境着实让我进退两难
esm的困境
项目的背景很简单,就是一个简单的vite+vue项目。
可能说到vite
,有些困难已经浮出水面了:
qiankun
诞生时确实考虑了兼容,但只是兼容了视图框架、浏览器,那个时代,乏善可陈的esm
生态加上浏览器对esm
糟糕的兼容,很难让开发者对此提起兴趣。但如今,除去webpack
的构建工具清一色的支持esm
。
而在esm
下,获取文件内容,包上一层去eval
的模式行不通了
好吧,解决方法并不是没有,一个最简单的思路就是让vite
不使用esm
,官方的legacy
插件可以让产物降级成systemjs
。
先不说这是否有脱裤子放屁之嫌,开发时的问题就绕不过去 -- 没有办法让vite
开发模式转成systemjs
。
一个更为人知晓的方法,是使用社区提供的插件,插件内部只是做了一些包装...简而言之,一个有点取巧、勉强能“跑”的方法。
当然,这个插件会遗失一些qiankun
的能力。但这是唯一能使用的,当时我决定,先在项目里用上这个。
看上去一切都还好,实际上遗失的能力也并没有带来什么麻烦,但这只是刚开始
糟糕的体验
为什么不用iframe
?所有的微前端使用者没办法回避这个问题。至少qiankun
认为,微前端方案是一个体验更好的iframe
。
在我使用的情形里,我需要在路由的文件中,也就是路由守卫的部分,去手动决定加载/卸载子应用(register的方法也一样),也就是路由适配的部分。
这是一种非常分裂的体验,我一边编写组件,一边去翻看activeRule,并确保相应路由下,挂载的dom存在,就如同在vue
中使用jquery
,格格不入。
一个库内部存在一种状态
是很正常的,可以封装一个组件,然后如何如何操作它,但现在我必须要把这种状态放在全局,我要管理的东西除了组件、状态管理以外,还要加上qiankun
的状态了
封装确实能解决问题,那为什么不直接封装
iframe
呢,
无用的沙箱
再接下来,我惊讶地发现,微前端中的“沙箱”
几乎可以说是微前端最招牌、最重要的能力,完全没有给我任何助益
- 先说css:
qiankun
对css的隔离,无论是两种模式的哪一种,都是不可靠的,这应该是一个众所周知的秘密,解决方法也很简单:给样式一个前缀,但这跟qiankun
无关。
- 再说js:
对js的隔离,先不说vite不vite的问题,qiankun
出名的三种沙箱(各种文章里会反复提及),只是隔离了window
这个变量,我并不认为js
隔离和window
隔离是一个完全等同的东西。
而这里的window
隔离,主要是为umd
结构服务,也就是一个应用,要往全局挂载一个变量,当应用卸载时,这个全局的挂载也要取消,
那在esm
模式下,即使可以eval
包裹,也毫无意义,因为它没有往全局挂载任何东西(除非你手动这么去做)
好吧,有些工具并不是为了提供功能/性能的,它可能是有一些托底的能力(比如模块联邦中的singleton),如果子应用存在某种不知原因的内存泄漏问题,框架能在卸载时强行处理掉,这真的是个很吸引人的点(虽然可能遇不到)。
很可惜,qiankun
志不在此。
- 还有
dom
:
框架提供了关于dom
的追踪能力,子应用卸载时,对应的dom
全部卸载,这好像是个很不错的功能。
可回头看一眼,子应用中,dom能来自哪里呢,无非就两种:被vue
管理的各种各样dom
,以及link
标签等静态资源,在生命周期里,vue
实例unmount
,控制的dom全走了,link(假设是一个外联样式)中的样式,按照前面说的,有特定前缀,不会对其他应用产生影响,好像也没必要卸载
一个想法撞进我的脑袋:
如果我直接把子应用的main入口打包,通过npm,在主应用对应位置,调用生命周期/直接挂载,引入style,会不会也行?
技术转型
先别急,我没打算用npm,
npm加载,如果是monorepo自然皆大欢喜,如果是在不同仓库,也可以模块联邦,这样开发时就没什么问题了。而在其他方面,这并不是也行
的级别,实际上要好得多:更加优越的性能,更贴合组件化,甚至少了qiankun这一层,更好的debug...
而在qiankun里,我必须要做一些额外的事:css前缀、降级、应用实例管理、路由适配、eval的性能消耗...
但我很难讲我究竟获得了什么,或许qiankun
处理了一些边界条件,但这优势好像不够大
这个时候我已经感觉很荒谬了,开倒车竟然是个最优选择,我一定在哪个地方出了问题。
让我们回头复盘一下:vite
+vue
,项目的技术栈算是正当的,或许更换一下微前端的技术栈,会让问题有所缓解。
那先把qiankun放放吧
护甲
我并没有针对qiankun,实际上,所有传统微前端方案,包括garfish,micro-app,都一样。
wujie的困境
就当时的情况,无界对esm
的高支持度很吸引我
不过在微前端的问题上,无界给出了完全不一样的答案,
传统的微前端体系,其实并不在乎隔离,而在乎影响的消除,
无界则专心于隔离的实现,或者说iframe给完全意义的隔离提供了可能:
把
document
隔离了,所有dom自然就在web component内部,卸载时,把web component卸载就行,(虽然它不是第一个这么做的)shadowRoot
下,样式天然隔离,iframe
下,只要卸载iframe,所有js的影响,包括内存泄漏等,自然解决,且支持esm
不在需要路由适配,同一个路由下,可以多个子应用,给组件化提供了可能
简而言之:即隔离+组件化。这着实给了很大的想象空间
但问题并没有消失,至少在当时,无界还处于rc
的状态,阅读一下其issue,可以看到这些问题:
这上面的issue都是已经close的,有彻底解决的,也有难以处理的。
关键之处在于,这些问题种类,都会衍生出各种各样的问题(并不是只有现在这么多),其中如 1
的部分,近乎无法解决,如2
、4
则难以解决,如3
,5
则有损体验
这是一些比较严重的部分,还有一些不碍事但总归不舒服的点:
为了将root转为host,追踪样式,即又回到了qiankun等的模式
对esm的处理,不能说不好,因为根本没有处理,esm不支持custom fetch的问题没有得到本质的改变,插件等对esm无效,且由于location对象不能defineProperty,无法在子应用中隔离 ...
高昂的学习成本(因为使用者必须理解其基础思路,不然会很难用),加上一些无法处理的问题,实际上我并没有在无界中有更好的体验
微前端 << 微服务
不提这个项目了,也暂时先将微前端的各种技术放一放,退回到这个概念的本身--微前端发起之初,是想做前端的微服务,对吧?
但实际上,即使把当前的微前端框架做到完美,没有任何体验的负担,没有任何bug,微前端仍然不是前端的微服务:
微服务可以以任何粒度出现,可以在任何业务出现,与技术栈无关,对项目本身没有任何要求。
实际上后端在讨论的从来不是微服务框架的实现,而是微服务的划分与管理。
前端很难参与这个话题,微前端基本是和2b强绑定的概念,就是各种大的重的管理系统,技术栈上,最多就只能兼容spa的各种框架,ssr、微模块等情况几乎毫无办法...
微前端的实现本身就已经极度痛苦了,没有办法分心其他
总结
当前的微前端存在问题,至少在这一点上,我们应该可以达成共识。
正如一场考试,现在我们已经知道题目是什么了,可以开始作答了