微前端在大数据产品中的应用背景和应用原理

描述

 

导读:本文由梯度科技前端研发部高级开发工程师贺信撰写,主要介绍如何基于前沿开源的前端技术方案实现微前端在大数据平台中的应用落地,并对所取得的应用效果进行剖析。主要包括以下几个方面:

案例背景

微前端解决方案对比和决策

实例应用剖析

无界框架源码与原理

实践总结

 

一、案例背景

根据公司产品某项需求,需要引入一个新应用作为一个独立模块集成进已有大型平台。公司本身产品的前端技术栈与待引入的新应用技术栈不同,分别采用的是vue和react,使用的UI框架也截然不同。新应用可以独立开发部署,但为了产品体验的一致性,需要将新应用嵌入已有业务中,并通过少量的改造使产品功能融为一体。

1.0阶段:采用外链跳转的方式,点击菜单打开新tab页展示数据开发模块功能

2.0阶段:采用iframe内嵌页面的方式,点击菜单在当前页面通过iframe加载url

3.0阶段:采用wujie微前端方案进行父子应用改造,组件式加载子应用方便快捷

4.0阶段:利用微前端能力实现模块拆分节约50%工作量,新增跨模块功能不再受限于技术栈

在引入wujie微前端方案后,对大数据平台产生了积极影响:

收益变现

主框架不限制接入应用的技术栈,微应用具备完全自主权,做到新技术选型时“技术栈无关”。

微应用仓库独立,部署完成后主框架刷新时自动完成同步更新,每个微应用之间状态隔离,运行时状态不共享,独立运行时。

在面对各种复杂场景时,对已经存在的系统做全量的技术栈升级或重构不现实,而微前端是一种非常好的实施渐进式重构的手段和策略,能够做到增量升级。

便捷的alive保活模式,可以确保在用户经常切换路由的场景下不丢失编辑状态,非常适合大数据平台的离线开发、实时开发、算法开发、低代码等业务场景。

持续增值

开启preloadApp预加载后,能显著减少白屏时间,结合保活模式可获得极速的打开体验。

支持 vite 等 ESM 脚本运行。

提供无感知的iframe降级方案,理论上兼容到IE9,配合插件系统能解决99%的兼容问题。

未来接入其他第三方应用(如BI、3D)将成为刚需,可无负担的扩展子应用。

二、微前端解决方案对比和决策

可能有人会问目前市面上主流的微前端方案有很多,为什么选择wujie微前端方案呢?该如何对比和决策?在这之前先来了解一下什么是微前端:

什么是微前端?

微前端是多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略,英文名称叫 micro-frontends。

2016年,首次提出“微前端”的概念,微服务这个被广泛应用于服务端的技术范式开始扩展到前端领域。

趋势:前端越丰富复杂,项目越庞大难以维护。

核心思想:独立的团队负责特定的业务和开发独立的功能模块。

架构对比

模块

需要注意的是,这些技术并不是相互排斥的,实际应用中可能会使用它们的组合来实现应用程序的需求。例如,同构应用可以与JAMStack和微前端一起使用,以提高应用程序的性能、可维护性和可扩展性。

实践经验告诉我们,永远不要追求最好的架构,而要追求最不糟糕的架构。真实世界里完美的架构并不存在,架构也没有对错之分,只有根据环境进行利弊权衡后的最佳结果。

微前端决策

在选择何种方案决策的时候 我们整理了一份决策要素清单:

模块

参考这份清单可以全方位对比每个框架的优缺点,帮助选择最适合的方案。我们选取了目前市面上最流行的几种方案进行技术调研:

阿里开源的 qiankun 方案,基于 single-spa

京东开源的 micro-app 方案,基于 webcomponent + qiankun sandbox

欢聚时代开源的 EMP 方案,基于 webpack5 module federation

字节跳动开源 Garfish 方案,这是一个集部署、框架、调试于一体的全套解决方案

腾讯开源的 Wujie 方案,基于 WebComponent 容器 + iframe 沙箱

详细调研了各类方案的优缺点之后,我们根据主客观判断给出了一个评分表:

模块

最终经过多方面的对比和分析,并且在基于产品开发周期短、使用和改造成本低的实际情况下,决定选择wujie方案。

三、实例应用剖析

这部分内容主要介绍wujie的实施方案和细节,对应wujie使用以及具体开发过程中解决的问题和最佳实践。

控制台展示

从浏览器按F12调出开发者工具,可以查看大数据平台实施微前端改造后的父子应用前端代码结构:

模块

从元素窗口看到,引入wujie后html里面的实际结构,其中wujie-app就是wujie创建的webcomponent自定义组件,可以理解为当前位置之前使用的iframe标签,现在把标签替换为了wujie-app,里面的内容就是子应用也就是引入的新模块的内容,外面的内容就是父应用也就是原来已有的部分。

另外可以看到该自定义组件里面只有dom元素和css样式内容,最关键的js代码去哪里了呢?继续往下翻阅代码到底部,发现在最底下多了一个iframe标签,这个标签的路径是当前网页的路径,样式是隐藏的状态。

模块

子应用的js是通过一个iframe沙箱的形式加载进来的。在wujie微前端框架的影响下,子应用的js和dom元素以及css样式是分开的。

wujie使用

wujie的组件式加载方式非常方便快捷,本部门将介绍wujie的vue组件的使用方式。

模块

wujie基于vue2、vue3、React框架的组件封装了对应的npm包:wujie-vue2、wujie-vue3、wujie-react

父应用安装并全局引入wujie-vue2。

常规的使用组件方式和props传递参数,与普通组件别无二致。

子应用本身无需安装任何包,无界对子应用注入了$wujie对象,可以通过多种方式获取。

也就是说在满足跨域条件下子应用可以不用做任何改造,可以直接把iframe替换为wujie组件。

需要特别注意一点的是wujie的运行模式,子应用是否开启了保活以及是否进行生命周期的改造会进入完全不同的处理流程。

模块

实际应用方案

以下是我们根据项目实际情况使用的实施方案和原因理由,需要说明的是在不同公司不同项目是完全可以根据需求和wujie提供的各种能力来灵活调整方案的,这里并没有什么最佳实践,个人认为只要能很好的满足需求就都是最佳实践。

保活模式

选用保活模式的原因主要有以下几个方面:

子应用模块代码加载量大,保活模式只需一次加载,避免频繁切换白屏。

用户在子应用中使用monaco编辑器编辑内容时容易忘记保存,用户不希望退出当前页面就丢掉了未保存的输入内容。

子应用模块里面的底座是用了另外一个高度封装的Web IDE UI框架,生命周期改造需要修改底座源码,改造起来需要投入大量的时间成本。

props通信和EventBus方式结合

我们需要用到子应用模块里面不止一个页面,且某几个页面要独立出来,方便后续做权限控制。

通过location.query参数不同判断展示

通过props传递模块类型和jump方法

通过bus.$on和bus.$emit监听和触发子应用的路由变化

两个wujieVue组件

3.0阶段后需要将子应用模块拆分成两个模块,分别有两个入口,所以用两个组件来区分。

父应用有两个wujieVue组件,每个组件对应多个页面,props传不同参数。

子应用只需要一份代码,巧妙的通过参数区分接口层、业务逻辑层,并在内部发生路由跳转时通知父应用处理,节约了50%以上的开发时间和人力成本。

服务代理

模块

这是父子应用独立部署两个服务的情况图,父子应用之间用nginx做路由跳转和接口代理

页面结构

模块

在页面结构示意图中可以看到绿色部分即原有平台页面,菜单导航还在原有代码里,而新增的蓝色部分就是用wujie包裹的部分。

数据流程

模块

这是我们的路由和参数传递流程,可以看到我们是用props和EventBus路由同步逻辑的。

为什么我们没有采用官方提供的路由同步功能,而是手动实现了一套路由同步逻辑呢?主要原因是由于子应用(第三方模块)不方便进行生命周期改造。

根据官方文档介绍,只有无界实例在初次实例化的时候才会从url上读回路由信息,一旦实例化完成后续只会单向的将子应用路由同步到主应用url上。但在开启路由同步后,刷新浏览器或者将url分享出去子应用的路由状态都不会丢失,会导致子应用永远也不会刷新。在实际应用过程中我们的多个页面要通过query参数来区分不同页面,因此必须通过更新路由状态来切换不同的页面。

四、wujie源码与原理

接下来我们更深入的研究一下wujie的源码和原理,对如何更好的使用微前端大有裨益。

wujie-core代码结构

模块

可以看到wujie的核心代码也十分简单,总共14个文件,入口在index.ts,可以顺着入口一点一点深入源码进行了解。

wujie-core核心代码思维导图

模块

js沙箱和css沙箱链接原理和细节

子应用的实例instance在iframe内运行,dom在主应用容器下的webcomponent内

无界在底层采用 proxy + Object.defineproperty 的方式将 js-iframe 中对 dom 操作劫持代理到 webcomponent shadowRoot 容器中,可以实现两者的互联,开发者无感知也无需关心。

细节:document的查询类接口:getElementsByTagName、getElementsByClassName、getElementsByName、getElementById、querySelector、querySelectorAll、head、body全部代理到webcomponent,这样instance和webcomponent就精准的链接起来。

当子应用发生切换,iframe保留下来,子应用的容器可能销毁,但webcomponent依然可以选择保留,这样等应用切换回来将webcomponent再挂载回容器上,这样子应用就可以获得类似vue的keep-alive的能力。

webcomponent和proxy的降级方案

在非降级场景下,子应用的dom在webcomponent中,运行环境在iframe中,iframe对dom的操作通过proxy来代理到webcomponent上。

而webcomponent和proxy IE都无法支持,wujie采用另一个的iframe替换webcomponent,用Object.defineProperty替换proxy来做代理的方案。

降级的行为由框架判断,当浏览器不支持时自动降级。

降级后,应用之间也保证了绝对的隔离度。

代码无需做任何改动,之前的预加载、保活还有通信的代码都生效,用户不需要为了降级做额外的代码改动导致降级前后运行的代码不一致。

五、实践总结

常见问题

(1)跨域问题和cors设置

可能的原因分析:

子应用的资源和fetch接口的请求都在主域名发起,所以会有跨域问题,子应用必须做 cors 设置。

资源或接口请求没有携带 cookie子应用本身是用fetch发起请求,需要将子应用fetch的credentials设置为include,这样cookie才会携带上去。

或者在主应用自定义fetch并将fetch的credentials设置为include。

(2)子应用弹框位置不正确

冒泡系列组件(比如下拉框)弹出位置不正确。

解决方案:子应用将body设置为 position: relative 即可。

子应用弹窗根据点击事件的 event.clientY 来确定top位置,但是主应用头部有导航栏导致位置计算不准确。

解决方案:子应用弹窗dom元素添加 position: fixed 样式即可。

社区优秀插件

wujie-polyfill

由于wujie(无界)采用的是WebComponents + iframe 来是脚本沙箱和样式隔离,该仓库用于弥补该方案的在特定的景下的不足。

插件列表:

 LocationReloadPlugin (页面刷新插件)

 EventTargetPlugin (事件目标插件)

 WindowGetterPlugin (window获取插件)

 WindowMessagePlugin (window通信插件)

 DocFullScrollPlugin (全屏插件)

 InstanceofPlugin (原型链判定插件)

基本上目前常见的子应用各种问题和坑都能在社区找到解决方案。

以上,我们介绍了微前端在大数据产品中的应用背景、应用理由、应用方式和应用原理,当然我们在实践过程中也不是一帆风顺的,中间也踩过不少坑走过不少弯路,也遇到过一些非常棘手一时无法解决的问题,所幸还有很多社区伙伴给予了很大的支持和帮助,梯度科技也将积极参与开源社区建设工作,回馈开源社区,为开源社区持续提供优质的代码与独立的开源项目。

        责任编辑:彭菁

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分