微前端之二 • 微前端的选型与接入
目前流行的几个微前端框架
-
Single SPA。它的官网将自己定义为 “一个用于前端微服务的 javascript 路由器(A javascript router for front-end microservices)”,实现了一套路由监听并加载子应用的功能,并且需要子应用暴露特定的注册、挂载、卸载等接口来完成与父应用(基座容器)的动态对接,依赖特定的模块管理系统,例如 Systemjs 来辅助完成这一过程。这种实现称之为“组合式应用路由分发”,是目前应用最广泛的一种方案。
-
Bit。从 Github star 数量来看,Bit 目前是关注量最多的微前端实现, 它的定义是 “Bit 是一个用于可组合软件开发的工具链,有了 Bit,你可以用组件构建任何东西”。Bit 的理念是组件驱动开发,你可以使用喜欢的框架比如 React、Vue 按照它的规范开发组件,并发布到 Bit Cloud,在应用构建时将他们组合到一起,这一点有些类似 Webpack。Bit 提供了完整的工具链,包括 testing、CI/CD、Cloud(组件托管平台)等,除了 Web 应用它还支持输出 APP、桌面程序、甚至是物联网程序,Bit 更像是一个平台,而不是纯粹的微前端框架。
-
QianKun。是一个基于 single-spa 的微前端实现库,旨在能更简单、无痛的构建一个生产可用微前端架构系统,简单说就是在 Single SPA 的基础上做了一些优化。
与 Single SPA 最大的区别有两点:
① QianKun 支持 HTML entry,而前者只支持 JS entry。JS entry 指的是子应用只暴露经过打包的 JS file 供基座容器使用,它自身并没有 HTML file,所以子应用需要额外添加一个 JS 入口文件导出 Single SPA 所需要的生命周期函数,有一定的侵入性。另外子应用即使做了组件按需加载的优化,打包了多个 JS chunk 文件,容器基座也会一股脑地去下载这些 JS chunk 文件,而 HTML entry 首先加载的是子应用的 HTML file,拿到的是一个完整的 SPA 应用,原有的特性得以保留,也不用改造子应用。
② QianKun 提供了 JS 沙盒机制,子应用之间来回切换不会对全局变量造成任何污染。 -
Webpack5 + Module Federation。Module Federation 插件是 Webpack5 的一大亮点,它允许应用 A 在运行时从应用 B 里面动态导入组件,解决了用 NPM package 方式共享的不便利,并且不需要基座容器,正如它的名字“模块联邦”。另外 Single SPA 子应用之间无法共享 package, 造成 JS 体积的总和增大,而 Module Federation 导出组件时可以设置共享的 package,如果使用方已经存在就不会再次加载,避免了重复引用的问题。
我们在选型的时候,Module Federation 还是处于 Beta 状态,不适合在生产环境使用;Bit 对代码的侵入性过强,不适合改造老项目;QianKun 刚刚创建;而 Single SPA 是已经很成熟的方案,所以它是我们的不二选择。
如何接入?
业务的推进不会因为拆分项目而停下来,开发资源只有一小部分时间投入到拆分中,所以需要制定一个详细的计划一步一步的拆解巨石应用,意味着在整体完成之前相当长的一段时间里,老项目和微前端会同时运行。
下图我们转到微前端前页面的布局图,左侧是 Menu 栏,每个 Menu item 对应一个产品,点击会切换路由并跳转到不同的产品。右侧分为上下两块,上面部分的左边一般是当前页面产品的名字, 右边是个人设置的按钮,下面一大块则是页面的详情。
考虑到 Menu 和 Heading 一直需要显示,可将它们剥离出来作为基座应用,在切换路由加载子应用的时候避免重复加载这部分的静态资源,对于每个子应用都需要用到的数据,比如用户的基本信息,建议放在基座应用请求,加载子应用的时候传给它,减少不必要的 http 请求。 然后每个 Menu item 对应的产品都作为一个 Micro front end App ,拆分之后变成下图这样,蓝色部分是基座应用,灰色部分是子应用。
我们网站 URL 的风格是 example.com/app1/product-a/xx ,app 是固定不变的,product-a 是产品的名字,后面的 xx 是页面的地址。
在拆分 product-a 的时候,我们将它的 URL 改成 example.com/app1/product-a/xx。Nginx 匹配到 /app1 则返回基座应用的静态资源,此时页面会出现白屏等待资源下载完成。基座应用加载完成之后,页面会出现 Menu + Heading,详情页依然处于 Loading 状态。基座应用按照子应用的注册信息拼接出 product-a 最新版本的 Js 文件地址并下载,这个过程完成后页面才会完全显示出来。待所有的产品迁移完成之后,再将 /app1 改回 /app。
统一技术栈,虽然微前端不要求使用相同的技术栈,但为了开发和维护的方便我们约定好统一使用 React 全家桶。另一方面,我们自研了一套 UI 组件,产品页面上任何一个可以见到的元素都是来自 UI 组件库,这样做能完全统一整个网站的视觉效果,而所有的组件都是用 React 实现,如果子应用选择 Vue 就意味着无法使用组件库。