乐趣区

关于前端:可能是多端适配性最好的微前端框架

引子

对微前端方向有理解的同学可能都看过 qiankun 的文档,深刻的同学预计也去翻过其代码和 single-spa 的源码。一年前我也是受微前端思维的影响,过后刚好面临的业务是变动频繁,须要反对线上性能的热插拔和独立部署。充沛调研了微前端和 qiankun 之后,发现很适宜以后业务,才开始开始实际微前端的。因为 single-spa 太过于简陋,就采纳 qiankun@1.x 作为微前端驱动,明天想把这段时间以来遇到的问题和解决之道跟大家分享一下。

可能面临的业务场景

假如你当初保护或新开发这样一个我的项目,业务变动频繁,页面性能有反复,但格调是统一的,临时只有 pc 端。你拿到设计稿后发现,能够把业务分拆成多套 layout,由基座 (主利用) 提供。性能独立的模块集成在子利用,每个子利用有非凡的 routerBase。

咔咔咔,两三下你做完了所有工作,子利用也能在线配置,反对热插拔和独立部署。爽歪歪~

几天后忽然有了以下几个新业务场景,你陷入了深思。。。

场景一:随着业务的变动,须要反对挪动端。

你新建了个子利用,routerBase 设置为 h5。当判断是挪动端时主动跳转 /h5/xxx。如果 pc 端拜访 /h5/xxx 时,主动重定向到 /xxx,能勉强应酬该场景,不过因为基座太重导致挪动端渲染很慢,在挪动端应用“后退 / 后退”按钮也会因为重定向太多导致凌乱,有时候页面渲染成 pc 页面或无奈渲染。

场景二:页面是响应式的,挪动端点击登录后,也能共享登录态。

响应式页面是流动页,有流动报名,就要求登录态共享。当用户在挪动端点击登录时,理当跳转到挪动端的登录页面,登录完重定向到流动页面 须要是登录胜利的状态。如果是每个子利用都有 routerBase,来回跳转好几次,而且还要想方法共享登录。

场景三:老板要求 pc 端和挪动端 URL 统一。。。

routerBase 人造断绝了这种可能性,根本无法做到!

场景四:老板要求一个四年前的我的项目聚合到门户站点中。。。

该项目标很多依赖特地是脚手架都曾经重大过期,锁定一级依赖的版本但锁不了二级以上的版本,导致构建公布失败。根本无法往我的项目代码中退出 bootstrap/mount/unmount…!

qiankun 的“kun”(困)境

qiankun 的定位可能是中后盾我的项目的聚合。这类我的项目彼此间很少依赖,登录态的管制能够在基座中实现,属于一个基座下的多个串联。但面临上述场景时,就显得力不从心。

另外上述场景也裸露了 qiankun 的诸多问题:

  • 基座个别由实在的业务利用承载,提供登录等性能,也提供多套 layout;
  • 基座很容易耦合很多 pc 端的 UI 库和性能代码;
  • 基座很容易沦为通用数据和逻辑的聚集地,尽管这些不是基座利用的职责;
  • 重大依赖 routerBase,框架判断便当但就义了灵活性;
  • 多实例共存反对有余,目前仅反对 microApp 模式,且这种利用不倡议有路由零碎;
  • 重大依赖生命周期 bootstrap/mount/unmount;

破局之道

@icatjs/micro 是我这四个月来实现并在理论我的项目中实际的新一代微前端框架,岂但能轻松应酬上述“窘境”,还具备其余良好的个性。目前仅有两个办法 registerSubapps 和 start,其余须要留神的就是子利用的配置了。TA 是如何应答上述窘境的呢?听我一一分享下:

端利用(或称 layout 利用)

大家看到这个新名词,不要认为对子利用做了强制的分类。只有某个子利用 a 提供了 layout 或一些门路被用作了 layout,又被其余子利用 b 依赖了,那么该利用 a 就可以看做是“端利用”。a 是不是端利用,本人基本不晓得,它会按子利用失常的加载。而 b 会在 a 提供了挂载点后再渲染到页面上。看个例子吧~

这是 端利用 的配置

{
    "name": "a",
    "entry": "aaa",
    "history": "browser",
    "props": {},
    "rules": [
      {
        "rule": "/",
        "container": "#mountNode",
        "endType": "pc"
      }
    ]
  }

解析下:a 作为 pc 端的利用,门路规定配置的是通配符 /,这样所有的路由都会被这个利用匹配到。当然会通过 endType 优先判断是哪个端,而后再去解决利用的“依赖链”。目前因为场景比较简单,每个路由下只反对一条依赖链。实践上讲可反对多条依赖链,也就是 多链多实例同时并存。

这是 子利用 的配置

{
    "name": "b",
    "entry": "bbb",
    "history": "browser",
    "props": {},
    "rootVars": {
      "externals": {
        "@react": "React",
        "@react-dom": "ReactDOM",
        "userInfo": "userInfo"
      }
    },
    "rules": [
      {
        "rule": "/b/activities/1520",
        "layout": "a > /layout/headless",
        "endType": "none"
      },
      {
        "rule": "/b/tec-support",
        "container": "#mountNode",
        "endType": "none"
      },
      {
        "rule": "/development",
        "layout": "a > /layout/basic",
        "endType": "pc"
      }
    ]
  }

也解析下:能够看 rules 中配置了三个典型的规定,第一条依赖了端利用 a 的 /layout/headless,第二条不依赖任何端利用,第三条依赖了 a 的 /layout/basic。

不配置 container 的门路,如果是子利用默认是 #subappMountNodeWrapper,如果是端利用不依赖任何 layout 则默认是 #appMountNodeAndDoNotCover。这样如果页面中有这些节点呈现时,子利用就会渲染到该节点上。

大家也能够看到,一个子利用的激活与否只和门路规定无关,这些规定恪守 single-spa 的 activeWhen 规定,能够是字符串、函数或数组。而每条门路规定都有本人的 container,也有属于本人的端 endType 和 layout。这样一个子利用既能够作为依赖链上的一个端利用,又能够作为独立的子利用。按门路规定灵便组合,适应多种场景须要。

留神!端利用也能够依赖其余端利用,只须要在其门路规定上配置须要依赖的门路即可。

entry

能够是一个 html 文件门路,也能够是一个数组,蕴含多个 js、css 和 html。初始化挂载点时,以最初一个 html 的内容填充进去。

全局路由零碎

内置解决了 react/vue 多个子利用同时被激活,多个路由零碎抵触的问题。无需再像应用 qiankun 那样,还要把子利用的路由配置到主利用中,以防抵触。全局路由会拦挡路由变动,把实在门路和 layout 门路散发到各个子利用,使其能失常渲染。
并且,所有子利用都能够有本人独立的 404 页面。能够把子利用齐全看成是独立的利用,无需顾虑路由的默认匹配,无需放心路由不匹配时间接进入利用的 404 页面。

沙箱

内置了 iframe 沙箱,子利用的代码运行是在 iframe 中。这样代码的状态都会保留在 iframe 中,不必麻烦提供快照,人造具备快照能力。也内置了垃圾回收队列,当一个子利用 5 分钟内不被激活,会被开释掉 iframe。
留神!这些 iframe 都是空白 iframe,消耗的渲染资源很少。但框架在 iframe 中提供了虚构 BOM,使这些字利用运行完的后果可能正确渲染到 iframe 外的实在渲染层中。
留神 2!子利用的所有对 dom 的操作都被限度在挂载点中,换言之,document.body 指向的是挂载点而不是渲染层的实在 body。这种紧密的管制也体现在 getElementById 等办法中。

当然还有其余一些个性如 第三方库共享 全局数据共享 组件 & 办法 & 状态流共享 缓存机制 等,首次介绍就先不深刻了。后续会筹备官网,全面介绍这些个性的。

到此,咱们梳理一下窘境的解决之道:

  • 场景一,须要反对挪动端。因为去掉了 routerBase 的依赖,又反对端利用,人造解决了基座重造成的渲染问题,重复重定向造成的跳转问题。
  • 场景二,页面是响应式的须要共享登录态。因为反对 layout 依赖链,某个端利用能够集中处理登录逻辑,其余端利用依赖其提供的性能即可。逻辑能够通过流共享模式或者全局数据共享,散发到多个子利用。响应式的页面属于依赖链的某个节点,天然能及时取得登录态。
  • 场景三,URL 一致性。因为去掉了 routerBase 的依赖,框架会在初始化时判断所处的终端,进而激活不同端利用。而端利用的门路规定都能够配置成通配符 /,这样很容易保障 URL 统一。
  • 场景四,四年前的老我的项目集成。框架反对无生命周期的我的项目,能够无缝渲染到门户站点中来。这是我测试的截图,大家有趣味能够看下:

这是集成的 2018 年的 seeconf 官网:

这是集成的口碑官网:

是否解决了 qiankun 的窘境呢?

因为通用逻辑和 layout 被摊派到一层层的“端利用”中,基座只须要做好对框架的援用和对规定的获取与解析,就能驱动起整体的站点运行起来。子利用的路由也不必顾虑抵触,而思考在主利用中 copy 一份。
基座能够做到很薄,每个端只需加载其须要的 UI 库或组件。比方 pc 端应用了 antd,挪动端能够应用 antd-mobile,框架不会把 antd 加载到挪动端去。

还有哪些有余?

  • 不倡议基座具备路由零碎,目前还没去试验基座路由和子利用路由抵触的场景。
  • 多路由零碎抵触计划,临时没有反对 angular,对 vue 的反对可能存在一点儿 bug。
  • 多条依赖链场景暂未反对,目前只解决了第一条依赖链。
  • 子利用中一些 js 灵便写法,如 eval 中的全局变量 var 去定义,包含自身的 var 定义的全局变量,还无奈正确挂载到虚构 window 上。可能存在某些我的项目加载报错的状况。
  • 梳理了 21 种路由抵触的场景,可能存在例外的状况。

浏览器兼容

支流浏览器 chrome, safari, edge, firefox 等都兼容,国内小众浏览器 360、qq 浏览器等也兼容。优先应用 Proxy,不反对则进化为 Object.defineProperty。

谁在应用

目前平头哥 IoT 团队在做芯片凋谢社区(OCC),这个框架的诞生也是为了满足其一直变动的业务需要。大家能够看下 pc 端和挪动端的 URL 是雷同的,但激活的子利用是不同的。

也心愿看到本文的同学,在将来某个时刻应用 @icatjs/micro 框架,做出精彩纷呈的作品来!

应一些同学的反馈,顺手搞了个示例 https//github.com/icatjs/icatjs-micro-demo, 请大家初步理解下该框架的应用。

退出移动版