Skip to content

在一切开始之前

我希望使用者对wujieqiankun有一些了解,这会让我的文档写得省力一些

这篇文章没有涉及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呢,

无用的沙箱

再接下来,我惊讶地发现,微前端中的“沙箱”几乎可以说是微前端最招牌、最重要的能力,完全没有给我任何助益

  1. 先说css:

qiankun对css的隔离,无论是两种模式的哪一种,都是不可靠的,这应该是一个众所周知的秘密,解决方法也很简单:给样式一个前缀,但这跟qiankun无关。

  1. 再说js:

对js的隔离,先不说vite不vite的问题,qiankun出名的三种沙箱(各种文章里会反复提及),只是隔离了window这个变量,我并不认为js隔离和window隔离是一个完全等同的东西。

而这里的window隔离,主要是为umd结构服务,也就是一个应用,要往全局挂载一个变量,当应用卸载时,这个全局的挂载也要取消,

那在esm模式下,即使可以eval包裹,也毫无意义,因为它没有往全局挂载任何东西(除非你手动这么去做)

好吧,有些工具并不是为了提供功能/性能的,它可能是有一些托底的能力(比如模块联邦中的singleton),如果子应用存在某种不知原因的内存泄漏问题,框架能在卸载时强行处理掉,这真的是个很吸引人的点(虽然可能遇不到)。

很可惜,qiankun志不在此。

  1. 还有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给完全意义的隔离提供了可能:

  1. document隔离了,所有dom自然就在web component内部,卸载时,把web component卸载就行,(虽然它不是第一个这么做的)

  2. shadowRoot下,样式天然隔离,

  3. iframe下,只要卸载iframe,所有js的影响,包括内存泄漏等,自然解决,且支持esm

  4. 不在需要路由适配,同一个路由下,可以多个子应用,给组件化提供了可能

简而言之:即隔离+组件化。这着实给了很大的想象空间

但问题并没有消失,至少在当时,无界还处于rc的状态,阅读一下其issue,可以看到这些问题:

  1. iframe的限制,#54,#308#230,#256#151310

  2. 模拟原生的困境,#172,#171,#49,#184

  3. 认知不同,体验分裂,#268,#79,#154

  4. 依赖内部的特殊处理,#205

  5. web component的限制 #206

这上面的issue都是已经close的,有彻底解决的,也有难以处理的。

关键之处在于,这些问题种类,都会衍生出各种各样的问题(并不是只有现在这么多),其中如 1 的部分,近乎无法解决,如24则难以解决,如35则有损体验

这是一些比较严重的部分,还有一些不碍事但总归不舒服的点:

  1. 为了将root转为host,追踪样式,即又回到了qiankun等的模式

  2. 对esm的处理,不能说不好,因为根本没有处理,esm不支持custom fetch的问题没有得到本质的改变,插件等对esm无效,且由于location对象不能defineProperty,无法在子应用中隔离 ...

高昂的学习成本(因为使用者必须理解其基础思路,不然会很难用),加上一些无法处理的问题,实际上我并没有在无界中有更好的体验

微前端 << 微服务

不提这个项目了,也暂时先将微前端的各种技术放一放,退回到这个概念的本身--微前端发起之初,是想做前端的微服务,对吧?

但实际上,即使把当前的微前端框架做到完美,没有任何体验的负担,没有任何bug,微前端仍然不是前端的微服务:

微服务可以以任何粒度出现,可以在任何业务出现,与技术栈无关,对项目本身没有任何要求。

实际上后端在讨论的从来不是微服务框架的实现,而是微服务的划分与管理。

前端很难参与这个话题,微前端基本是和2b强绑定的概念,就是各种大的重的管理系统,技术栈上,最多就只能兼容spa的各种框架,ssr、微模块等情况几乎毫无办法...

微前端的实现本身就已经极度痛苦了,没有办法分心其他

总结

当前的微前端存在问题,至少在这一点上,我们应该可以达成共识。

正如一场考试,现在我们已经知道题目是什么了,可以开始作答了

Released the MIT License.