![9c328d41d951e6c06f405037c4c27a85.png](https://i-blog.csdnimg.cn/blog_migrate/7c5eb113afc66606e34ae183a8937ed7.jpeg)
halo,大家好,我是 132,前阵子冥思了一会儿微前端,然后周六日趁热打铁,马上写了一个微前端框架,名叫 berial
然后今天是开源的第三天,得到了很多友情赞,昨晚正式发版了,请容我一一道来
微前端的核心和本质
俺之前有发过一篇文章:https://zhuanlan.zhihu.com/p/165002889
微前端产生的原因,说白了是个业务问题,我们的业务项目,分久必合合久必分,一个项目合在一起太久了,等到不好维护的时候,就需要拆分,反之同理,拆的太碎的时候,合在一起更容易维护
微前端的本质是前端路由,不需要和运维童鞋联调,而且自己还能控制生命周期,进行沙箱隔离,甚至可以状态通信
微前端框架大致套路差不多,大概包括下面几个 feature,请容我一一道来
lifeycycles
shadow dom
scoped css
Proxy sandbox
html-loader
global store
lifecycle loop
生命周期的是 single-spa 的核心,berial 内部也复现了一组类似的循环队列
load 阶段(microtask)
importHtml -> parser -> shadowdom -> proxy sandbox -> bootstrap()
mount 阶段(macrotask)
mount() -> umount() -> mount() -> unmount() -> ...
如上所见,这些生命周期,有的只执行一次,有的会每次执行一次交替,和我们常见的 event loop 非常相似,所以这个机制我们也成为 lifecycle loop
其中,load 阶段, 负责做的事情有点多,大概就是 import html 然后解析出 dom、css、script,然后将 dom 和 css 放到 shadow 中,然后给 script 套一个 sandbox,然后将 lifecycles 收集起来,这所有流程的每一个步骤,都是 promise,所以这是个微任务队列
然后之后的路由切换,就不断调用 mount 和 unmout 钩子,交替调用……
mount 中做渲染工作,比如 React.render()
, unmout 同理
最终,berial 实现了一套类似 event loop 的生命周期队列
shadow dom & scoped css
shadow dom 又名沙雕 dom,名副其实的沙雕,钢铁直男,一点也不弯
市面上几乎所有微前端框架都选择弃用沙雕,berial 的话却选择一起沙雕
这会导致很多问题,有时候需要手动改业务逻辑,但也能接受,我们做微前端框架,真的不是为了 0 改动
迁移,如果追求 0 改动,请移步 iframe
所以我认为,消耗一点点迁移成本,换来更好的架构设计,是容许的
用了沙雕之后,scoped css 浑然天成,而且非常硬,根本无法穿透(还是有 hack 办法的)
Proxy & Mutation Observer sandox
js 沙箱机制,是微前端框架另一个机制,微前端的沙箱一般来说是劫持 window
其中一种是快照沙箱,原理是对 window 进行多次遍历,达到激活和失活的效果,但这不适用于 berial,我们企图找到更好的抽象机制
另一种思路是 Proxy,这也是 qiankun,berial 正在使用的思路
其中有人借用了 iframe 的 contentWindow,去得到一个完全不同的 window
https://github.com/aliyun/alibabacloud-console-os/blob/master/packages/core/browser-vm/src/Context.js#L70
但是我认为这是不符合语义的,因为实际上,我们的沙箱是针对 js 的,而其他所有内容,比如 document,其实还是同一个 document
所以我们没必要全部重新实现一遍 window,但是这样就会有经典额逃逸问题
什么是逃逸问题呢,就是在 js 里加载一个新的 js,
const script = document.createElement('script')
document.head.appendChild(script)
这样,在头部添加一个 script,那么这个 script 就会从沙箱逃逸到外部 document 中
其他框架仍然是使用了 Proxy 进行劫持,berial 中使用了 Mutation Observer
我们侦测 document 的变化,如果变化的 target 不是当前沙箱,则对其进行劫持,然后塞回沙箱
html-loader
html-loader 也是 berial 的一个核心,灵感来自 qiankun,其实是一个 parser,然后找到 script 和 style,暴露出来
这部分目前是纯正则,因为这份匹配实际不需要走整套编译流程,相对 case 其实不是很多
有兴趣的童鞋可以尝试读一下,我们未来也会通过这个机制去探讨正则的更优写法和更高性能
global store
用于状态通信的最简机制,也是通过 Proxy 实现的,可以简单方便地在不同 APP 之间通信
然后我们还会自带一个批处理,让用户多次修改状态,mount 阶段都交替一次
different from qiankun?
和乾坤的不同在于,我们原生使用了沙雕,sandbox 的实现也更科学,而且不依赖 single-spa,所有代码都能自己控制,各个机制之间的优化空间更大
berial 虽然是个业务框架,但代码还是会延续我的风格,就是简单直白风,在追求业务的同时,我们还是会花时间研究底层架构的设计改进
webpack5 module federation?
这里真的很有必要提一下 webpack5 的新特性,中文名叫做「模块联邦」,令人稍稍有点沮丧的是,这玩意完全可以实现多个不同技术栈共存,而不需要任何框架
也就是说,如果你没有沙箱隔离需求,只是需要技术栈无关,那完全可以使用 webpack 自带的插件搞定
所以我现在的观点是,对于无法升级 webapck,代码逻辑很乱需要隔离的多技术栈,可以使用 berial / qiankun 这种 runtime 方案
如果是能够使用 webpack5,仅仅只是为了技术栈无关,代码共享,可以直接使用 MF
以上就是最近搞得全部了,最后放一下地址:
欢迎大家加入组织,未来 berial 也会社区维护下去,大家有啥新想法和玩法可以一起讨论呀