对微前端方向有了解的同窗可能都看过 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
routerBase 自然断绝了这种可能性,根本没法作到!github
该项目的不少依赖特别是脚手架都已经严重过期,锁定一级依赖的版本但锁不了二级以上的版本,致使构建发布失败。根本没法往项目代码中加入 bootstrap/mount/unmount...!chrome
qiankun 的定位多是中后台项目的聚合。这类项目彼此间不多依赖,登陆态的控制能够在基座中完成,属于一个基座下的多个串联。但面临上述场景时,就显得力不从心。npm
另外上述场景也暴露了 qiankun 的诸多问题:json
@icatjs/micro 是我这四个月来实现并在实际项目中实践的新一代微前端框架,不但能轻松应付上述“困境”,还具有其余良好的特性。目前仅有两个方法 registerSubapps
和 start
,其余须要注意的就是子应用的配置了。TA 是如何应对上述困境的呢?听我一一分享下:
你们看到这个新名词,不要觉得对子应用作了强制的分类。只要某个子应用 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。这样一个子应用既能够做为依赖链上的一个端应用,又能够做为独立的子应用。按路径规则灵活组合,适应多种场景须要。
注意!端应用也能够依赖其余端应用,只须要在其路径规则上配置须要依赖的路径便可。
能够是一个 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 等方法中。
固然还有其余一些特性如第三方库共享、全局数据共享、组件&方法&状态流共享和缓存机制等,初次介绍就先不深刻了。后续会筹备官网,全面介绍这些特性的。
到此,咱们梳理一下困境的解决之道:
/
,这样很容易保证 URL 一致。这是集成的 2018 年的 seeconf 官网:
这是集成的口碑官网:
因为通用逻辑和 layout 被分摊到一层层的“端应用”中,基座只须要作好对框架的引用和对规则的获取与解析,就能驱动起总体的站点运行起来。子应用的路由也不用顾虑冲突,而考虑在主应用中 copy 一份。
基座能够作到很薄,每一个端只需加载其须要的 UI 库或组件。好比 pc 端使用了 antd,移动端可使用 antd-mobile,框架不会把 antd 加载到移动端去。
主流浏览器 chrome, safari, edge, firefox 等都兼容,国内小众浏览器 360、qq 浏览器等也兼容。优先使用 Proxy,不支持则退化为 Object.defineProperty。
目前平头哥 IoT 团队在作芯片开放社区(OCC),这个框架的诞生也是为了知足其不断变化的业务需求。你们能够看下 pc 端和移动端的 URL 是相同的,但激活的子应用是不一样的。
也但愿看到本文的同窗,在将来某个时刻使用 @icatjs/micro 框架,作出精彩纷呈的做品来!
应一些同窗的反馈,随手搞了个示例 https//github.com/icatjs/icatjs-micro-demo, 请你们初步了解下该框架的使用。