手写一个微前端框架(内含源码地址)

来源:伊撒尔

https://zhuanlan.zhihu.com/p/169800579

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 sandox

沙箱机制,是微前端框架另一个机制,大概有两种方案

其中一种是快照沙箱,原理是对 window 进行多次遍历,达到激活和失活的效果,但这不适用于 berial

因为 berial 使用了沙雕,意味着它是一个多 app 并存的机制,无论如何都不能共享同一个 window,那,有没有办法呢?

答案是有的,我们一开始用了一个奇技淫巧,就是创建一个 iframe,然后劫持 iframe.contentWindow,等于是通过 iframe 得到了一份 window 的副本

但是这么做会引入额外的限制,比如 iframe 的跨域问题,通信问题等等,而且由于 iframe 的逼格实在太低,导致真的没人愿意用它

新思路是写时拷贝,这个思路来自 immer,就是我们全量 copy 一份 window 开销很大
于是我们写对象的时候,进行按需拷贝,当然和 immer 不同的是我们不需要构建一颗新的树

这样做有很多好处,除了去除 iframe 的限制,而且可以减少一层沙箱,而且还可以顺带增加一次大的便利,扩大优化空间

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

以上就是最近搞得全部了,最后放一下地址:

https://github.com/berialjs/berial

最后

欢迎关注「前端瓶子君」,回复「交流」加入前端交流群!

欢迎关注「前端瓶子君」,回复「算法」自动加入,从0到1构建完整的数据结构与算法体系!

在这里(算法群),你可以每天学习一道大厂算法编程题(阿里、腾讯、百度、字节等等)或 leetcode,瓶子君都会在第二天解答哟!

另外,每周还有手写源码题,瓶子君也会解答哟!

》》面试官也在看的算法资料《《

“在看和转发”就是最大的支持

你可能感兴趣的:(算法,队列,java,面试,xhtml)