介绍

Haploid.js 是一个微前端在新窗口打开框架,用来调度复杂需求环境下的独立微应用的运行。

微前端也称之为前端微服务,是一种将多个独立的前端应用在运行时进行组装的架构。构建 Haploid.js 的初衷是解决或者优化现有微前端工具上的问题和能力不足,以满足多样的业务诉求。

为什么需要它

我们最早在 2020 年夏秋之交开始部署微前端架构的应用。当时要解决的问题并非是存量应用的融合,而是基于对业务未来发展规模的判断,从初始设计上就要避免将来演变成“巨石应用”的问题。

当时可见的的现成方案是 single-spa在新窗口打开,以及在其基础上构建的 qiankun在新窗口打开

single-spa 严格来说并不是一个框架,它对自己的定义是前端微服务的路由器(router for front-end microservices)。要使用它,你必须解决一些诸如应用载入、CSS 管理等工程化问题,很难说开箱即用,因此 qiankun 是对其一个很好的补充和扩展,并且为后续出现的新框架树立了一个作为微前端框架的基本能力标杆。

我们的应用在 single-spa 的支持下也构建出了一个微型框架,但随着业务复杂度的增加,这种架构逐渐暴露出了很多不足。

首先是 单例模型的限制single-spa 的 API 全部都是单例的,这意味着你只能在一个页面上实现一个微前端实例。虽然可以配置为不同的应用有不同的 DOM 挂载点,但是分类管理仍然很麻烦,你需要手动区分哪些应用是共享一个挂载点的。另外如果有嵌套关系,因为 DOM 挂载点的缺失,应用很可能挂载失败。

事实上,目前大部分微前端解决方案都是单例模式的。

其次是 应用的调度效率single-spa 是基于单队列的的阻塞式调度模型的,也就是说,前一个应用的任务需要执行完才可以处理下一个任务。这将带来两个问题:

  1. 和单例模型相关,处于不同挂载点的应用之间不应该有阻塞;
  2. 未完成的过期任务会阻塞最新的有效任务,比如正在执行网络请求的旧应用 bootstrap 就会阻塞最新应用的启动

第三是 应用调度的健壮性,如何针对上述的调度优化处理不当,以及在一些特殊逻辑下,整个应用最后响应的效果并非符合用户最后操作的期望,参考 single-spa#953在新窗口打开single-spa#950在新窗口打开。而且我们坚信任何框架如果没有在此方面做特别的设计,那么就会有非常高的概率出现这种问题。

第四是 缺乏安全退出机制,与 single-spa 类似的一众解决方案,期望自己作为顶层路由,然后在有旧应用改造的需求之下,微前端实例往往存在于某一个 vue-router/react-router 之下,那么一旦路由发生变化,就需要一种安全的资源销毁机制。single-spa 虽然有反注册(unregisterApplication)机制,但是也仅仅作用于处于 MOUNTED 状态的应用,无法清理干净。

最后我们还需要一系列的针对 常见功能的细粒度优化,比如:

  1. 实现 异步导航撤销(Async Navigation Cancelation) 能力, 这是 single-spa v6 引入的特性,但存在错误;
  2. 增强 保活(Keep Alive) 能力;
  3. 增强 预载(Preload) 能力;
  4. 实现 死循环侦测(Dead Loop Detect)
  5. 增强对资源请求参数的控制粒度
  6. 等等

针对以上需求,我们构建了 Haploid.js,其中主要参考了 single-spa 的生命周期模型,因此你依然可以用 single-spa-vuesingle-spa-react 等工具来封装子应用,是兼容的。

Haploid.js 使用了超过800项案例进行测试,以保证其在应用调度上的正确性。