一个微前端库的诞生-0 | Rallie:微前端的另一种可能性

前言

2019年毕业后,入职了华为一个支撑下游产品线做网络运维产品的中台部门做前端开发。当时部门里的前端体系基于几年前留下来的一个微前端框架,我们组负责维护基于该框架开发的菜单,dashboard之类的物料应用供其他业务组使用。当时微前端这个概念还没有像现在这样在业界被广泛讨论,而部门里的这个微前端框架早在2015年就基本开发完成并在业务部门铺开使用,可以说思想还是非常前卫的(后来这个框架的作者当了我们大部门的cto)。然而,由于api设计得非常反直觉,代码可读性也实在太差,文档更是完全云里雾里,后续接手维护的人并没有将这个框架继续发展,到我入职的2019年,这个框架已经完全是毒瘤的存在,让开发们叫苦不迭。

我入职之后在学习这个框架的过程中也是非常痛苦,然而在大致搞懂其使用方式后,我发现它其实只是一个非常简陋的发布订阅模型而已。于是19年12月的时候,我尝试自己重新设计api,并从零开始动手实现了该框架的基本功能,当时把它命名为Obvious发到了github上, 然后作为教学项目给同事们讲解,帮助大家对照着理解部门内部项目的核心思想和原理。

这个过程让我我第一次体会到了造轮子的乐趣,后来虽然换了工作,但是也没有完全闲置这个项目,工作不是很忙的时候会思考怎么改进这个框架,有时候受到其他库的启发突然灵感乍现,就会动手设计并实现新的feature,就这样断断续续开发了两年,与最开始的教学模仿项目相比,无论是用法还是思想都已经发生了巨大的改变,项目名也因为不想跟已有的npm包重名又不想加个人npm账号的前缀,所以改成了Rallie,是集会的意思,寓意大型前端应用是一个一个前端微服务的集合。

前段时间给阮一峰老师的周刊投了稿被采纳了,引来了一波小高峰的流量,虽然点star的朋友们只是标记了一下,没有什么人实践,但还是让我很受鼓舞。最近公司的业务不是特别忙,有比较多空余时间,于是给Rallie写了个文档。回看这两年开发这个项目的过程,感觉还是收获颇丰的,实践了很多在公司项目没机会尝试的技术,发展到现在,个人认为也算是一个有自己思想和特色的微前端库。

所以决定在掘金上写一个系列的博文,讲解一下这个项目的核心原理,算是对这个作品的一次推广,也当作是这段开发历程的一个阶段性的总结吧。

首先放一下项目和文档地址供大家参考和学习:

本文正文部分会简要介绍项目的选型,总体架构等总览概括性的内容,后续的文章主要是分模块讲解实现原理,具体Api的使用方式可以参阅文档。希望这个项目能对读者有所启发,觉得有帮助的朋友可以给一个小小的star,也非常欢迎通过issue和pr的方式指出或者帮忙改进项目的不足。

正文

选型

typescript

Rallie从一开始就使用作为开发大型项目标配的typescript开发,静态类型检查可以帮助我们在开发时提前识别到很多问题。同时,使用typescript开发也让我在设计Rallie的功能时会多考量一下如何为用户提供良好的typescript支持。比如状态管理,早期Rallie的状态管理是基于字符串keyPath实现的,后来发现这条路子完全无法提供良好的ts支持,因此在后期转为基于@vue/reactivity做状态管理。另外,传统的事件发布订阅模型的实现方式对ts的支持也不够友好,因此我在后来引入了Proxy转发的方式来实现发布订阅。

当然了,由于我个人对typescript的理解还比较粗浅,其实项目里有挺多地方ts的使用还不够规范,可能甚至有错误,这个计划后期有空再深入地学习一下ts然后专门改进一下

打包工具

Rallie选用rollup作为打包工具,这个选型没做什么纠结。在开始选型的时候其实只会webpack,但是早有耳闻rollup比较适合打包js库,因此在选型的时候快速学习了一下rollup就上手使用了,而rollup确实比起webpack要简洁很多,打包出的代码甚至直接可读,的确非常适合作为开发第三方库的打包工具。另外值得一提的是,如果让我现在重新选型的话,我应该会选用vite的库模式,vite在构建生产版本时也是基于rollup的,但是它有一个优势是,我们在开发时可以非常方便地在index.html中写测试页面,这对我们快速验证功能非常有用。

当然了,由于我使用了monorepo分包,也可以很方便地在我的样例项目中快速查看效果,因此也就没有折腾换构建工具了。

monorepo

Rallie刚开始的时候叫Obvious,其实只是一个单体项目,后来这个项目逐渐变成了一个core,基于这个core衍生出后来的Rallie,后来想提供对React和Vue的支持,于是又开了@rallie/react和@rallie/vue两个项目,有了中间件功能后,想提供一些独立维护的中间件,又需要单独开项目。这些项目之间存在依赖关系,有时候一个包做了更改,会影响另一个包的功能,在分项目开发了一段时间后,发现要手动追踪依赖版本很麻烦,于是决定把这些项目收归到同一个仓库用monorepo的方式来组织管理,调研了一下发现lerna基本提供了我需要的功能,所以就选用了lerna做monorepo管理工具。

使用lerna的收益还是很明显的,项目间的依赖管理不再需要操心,发布也变得自动化了。

状态管理

状态管理作为Rallie服务化的一个重要组成部分,经历了几次演进。正如前言里所说,这一开始是一个模仿华为内部框架的项目,基于简单的发布订阅模式实现状态监听,因此最开始的状态管理功能类似这种使用方式

store.initState('key', value)
store.getState('key')
store.setState('key', value)
store.watchState('key', callback)
复制代码

后来觉得这样的状态管理太过简陋,于是在原来的基础上做了一点扩展,支持让key值传入对象的keyPath,类似这样

store.initState('key', {
  a: {
    b: {
      c: value
    }
  }
})
store.getState('key.a.b.c')
store.setState('key.a.b.c', value)
store.watchState('key.a.b.c', callback)
复制代码

但是这样做完全是面向字符串编程,对typescript的支持太不友好了,因此最后还是决定采用类似mobx的方式做状态管理。本来考虑模仿Vue3,基于Proxy自己实现响应式状态,后来在学习过程中发现其实Vue已经单独把响应式模块单独发布成npm包@vue/reactivity,于是就直接拿来用了。

单元测试

单元测试框架没啥说的,直接选用了大名鼎鼎的jest。值得说一下的是在给@rallie/vue和@rallie/react写单测时,我没有直接用@vue/test-utilsenzyme,而是选用了Testing Library下的@testing-library/react@testing-library/vue。@vue/test-utils和enzyme分别更贴近Vue和React,可以测试组件的状态等信息,而Testing Library的理念是不关注测试组件的细节,而是更贴近用户,关注组件渲染出的内容。使用Testing Library写出的Vue单测的代码和React单测的代码会很相似,可以降低我的学习成本和测试时的心智负担。

对于有组件单元测试需求的同学,个人还是非常推荐学习一下Tesing Library这个库的。

样例项目

在各个包的功能开发完之后,我还需要开发一个样例项目让学习我这个库的人能直接上手实践。我的设想是搭建一个mpa的项目,分成三个页面,一个页面用Vue,一个页面用React,还有一个页面把前两个页面的内容聚合展示在一起。经过调研,我觉得vite的多页面应用模式是最满足我的需求的。比起用webpack搭建既有React又有Vue的多页面应用,vite的配置要简洁太多。

项目结构

|---packages
    |---core
    |---rallie
    |---react
    |---vue
    |---load-html
    |---playground
复制代码

Rallie采用monorepo管理,目前主要分成以下几个包

  • core:实现最核心的应用编排,应用通信功能。发布为@rallie/core
  • rallie:基于core做了一层封装,帮助使用者不必关注整个应用集群的架构,只专注于自己应用的开发即可。发布为rallie
  • react:基于rallie,通过hooks将Rallie提供的服务与React框架结合。发布为@rallie/react
  • vue:基于rallie,通过compositionAPI和mixin将Rallie提供的服务与Vue框架结合。发布为@rallie/vue
  • load-html:一个相对独立的包。提供一个从html中加载资源的中间件。发布为@rallie/load-html
  • playground:样例项目。不发布为npm包

总体架构

rallie并不是一个难度很大的项目。经过后面的学习,你将会发现@rallie/core的代码设计遵循这样的架构

截屏2021-12-16 下午5.02.05.png

而rallie在@rallie/core的基础上进行封装,呈现这样的架构

截屏2021-12-16 下午5.02.16.png

敬请期待

个人认为目前市面上的微前端框架同质化有些严重,从qiankun开始,每个框架都很热衷于去研究应用隔离功能,微前端相关的文章除了介绍概念的,其他涉及代码实操的文章大概有八成是在讲js沙箱的实现。正如我在文档的比较部分所说,感觉各大厂的微前端框架渐渐都做成了另一种形式的iframe。也正因为如此,其实也有挺多人觉得微前端是没事找事,把简单的事情搞复杂了。而Rallie的特色是服务化和去中心化的思想,力求能帮助开发者通过微前端架构来提高前端页面或者模块的可复用性和可移植性。

或许学习了Rallie之后,你能从不同的视角窥见微前端架构的其他可能性。

猜你喜欢

转载自juejin.im/post/7042233392141697031