多是多端适配性最好的微前端框架

引子

对微前端方向有了解的同窗可能都看过 qiankun 的文档,深刻的同窗估计也去翻过其代码和 single-spa 的源码。一年前我也是受微前端思想的影响,当时恰好面临的业务是变化频繁,须要支持线上功能的热插拔和独立部署。充分调研了微前端和 qiankun 以后,发现很适合当前业务,才开始开始实践微前端的。因为 single-spa 太过于简陋,就采用 qiankun@1.x 做为微前端驱动,今天想把这段时间以来遇到的问题和解决之道跟你们分享一下。css

可能面临的业务场景

假设你如今维护或新开发这样一个项目,业务变化频繁,页面功能有重复,但风格是一致的,暂时只有 pc 端。你拿到设计稿后发现,能够把业务分拆成多套 layout,由基座(主应用)提供。功能独立的模块集成在子应用,每一个子应用有特殊的 routerBase。html

咔咔咔,两三下你作完了全部任务,子应用也能在线配置,支持热插拔和独立部署。爽歪歪~前端

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

场景一:随着业务的变化,须要支持移动端。

你新建了个子应用,routerBase 设置为 h5。当判断是移动端时自动跳转 /h5/xxx。若是 pc 端访问 /h5/xxx 时,自动重定向到 /xxx,能勉强应付该场景,不过由于基座过重致使移动端渲染很慢,在移动端使用“前进/后退”按钮也会由于重定向太多致使混乱,有时候页面渲染成 pc 页面或没法渲染。react

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

响应式页面是活动页,有活动报名,就要求登陆态共享。当用户在移动端点击登陆时,理应跳转到移动端的登陆页面,登陆完重定向到活动页面 须要是登陆成功的状态。若是是每一个子应用都有 routerBase,来回跳转好几回,并且还要想办法共享登陆。git

场景三:老板要求 pc 端和移动端 URL 一致。。。

routerBase 自然断绝了这种可能性,根本没法作到!github

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

该项目的不少依赖特别是脚手架都已经严重过期,锁定一级依赖的版本但锁不了二级以上的版本,致使构建发布失败。根本没法往项目代码中加入 bootstrap/mount/unmount...!chrome

qiankun 的“kun”(困)境

qiankun 的定位多是中后台项目的聚合。这类项目彼此间不多依赖,登陆态的控制能够在基座中完成,属于一个基座下的多个串联。但面临上述场景时,就显得力不从心。npm

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

  • 基座通常由真实的业务应用承载,提供登陆等功能,也提供多套 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 等方法中。
image.png

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

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

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

这是集成的 2018 年的 seeconf 官网:
thead&seeconf.gif

这是集成的口碑官网:
thead&koubei.gif

是否解决了 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, 请你们初步了解下该框架的使用。

相关文章
相关标签/搜索