乐趣区

关于微前端:GrowingIO-智能运营产品微前端实践

作者:俞展弘
GrowingIO 前端开发工程师,次要负责智能经营团队前端开发、gio-design 开发

引言

GrowingIO 智能经营产品,是 GrowingIO 为客户经营团队提供的一站式、精细化经营治理与数据分析平台。

近期,GrowingIO 智能经营产品团队须要将站内触点等经营性能从 SaaS 平台移植到增长平台上,以反对私有化部署(On-Premise,简称 OP)计划在客户环境中的进行,以此为背景,GrowingIO 经营前端团队在微前端的实际上走了本人小小的一步。

1. 什么是微前端

尽管「微前端」三个字这两年曾经在前端畛域被大家所宽泛熟知,但本着谨严写文章的精力还是须要在这里简略阐明一下。

微前端的概念脱胎于服务端的「微服务」概念。最近几年前端世界的趋势就是构建一个功能丰富、弱小的浏览器应用程序(SPA)。

个别一个前端利用通常会由多个独立的团队开发,随着工夫的推移,前端利用的代码规模会一直增长,并且体现得越来越难以保护,成为一个谁看都头疼的巨石利用。

微前端呈现的意义在于将这样一个繁多的巨石利用,转换为多个较为小型的前端利用再聚合为一。各个小型利用能够独立开发、部署,乃至于独立抉择本身的技术栈与依赖,而不影响其余兄弟利用与父利用。

在此基础上简略总结一下微前端须要做到(或者说带来的益处):

  • 利用自治 :只须要遵循对立的接口标准或者框架,各个利用能够集成到一起,同时相互之间是不存在依赖关系的。
  • 繁多职责 :每个前端利用能够只关注于本人所须要实现的性能。
  • 技术栈无关 :能够在应用 Angular 的同时,又能够应用 React 和 Vue。

2. 为什么要拆分微前端

如同上一段所说,目前的 GrowingIO SaaS 主站前端利用也是一个随同迭代变得越来越大的巨石利用,但对于经营的前端团队来说,除了公共办法平时不会涉及到大多数不相干模块。

其次,在以后以及可预感的将来,在 SaaS 环境与 OP 环境中经营的业务场景在大部分状况下是雷同的。然而对于前端团队来说,OP 与 SaaS 差异包含但不限于:

  • 采纳了全新脚手架
  • 用 GraphQL 取代了 RESTful API 获取根底资源
  • 根底组件版本与路由等基础设施与 SaaS 有破坏性更新
  • OP 不须要全副的 SaaS 性能

如果采取最原始暴力的开发方式(copy),就须要开发保护两套经营平台前端代码,一套在 SaaS 前端仓库,另一套在 OP 前端仓库。

这样会导致同个性能须要保护两套 codebase,工夫久了 OP 仓库的代码会离 SaaS 骨干代码越来越远,导致保护艰难,同时有概率会一个性能写两次,这对于一个团队来说是不可承受的。

从理论需要登程,咱们当然心愿能做到一套代码适配两个环境,对于同一个性能开发、bug 修复都只须要一次工作量,这样能大大提高人效。所以在性能移植前须要将前端我的项目进行微前端革新。

3. 智能经营产品微前端一小步

实现智能经营产品(下称 Marketing)的微前端利用过程大抵划分为两步走:拆 → 装,与此同时解决各种抵触笼罩和兼容问题。

3.1 拆分

拆分的过程中咱们须要对 Marketing 利用与 SaaS 利用进行解耦,并针对 OP 环境进行差别屏蔽。

OP 和 SaaS 当中,存在一些接口不雷同,所以须要做一层兼容解决,这部分放在了 @gio-bootstrap 以及 resourceService 当中实现,目标是使上层业务对环境无感知。

@gio-bootstrap

从 SaaS 独立进去 Marketing 仓库的最后承继 SaaS,权限、我的项目、用户等根底数据在 .ejs 模板文件中进行获取,不容易浏览与保护不说,且复用艰难,并妨碍了 webpack 的降级优化。

因而咱们抽取了一个对立的 bootstrap 办法实现基座利用不同时的根底资源申请:

 type GioBootstrap = (platform: 'saas'|'op') => Promise<ResourceMap>

办法内依据与基座利用团队约定确认的资源列表以及接口进行每个资源的获取。针对单个资源,应用一个 requestGenerator 来进行资源申请函数的创立:

/**
 * 单个资源申请前会遍历 dependencies 列表资源是否都已存在,针对未申请的资源进行申请获取,已申请过的资源会从 sessionStorage 中进行获取;* 为了兼容现有代码,@gio-bootstrap 同时在 sesstionStorage 与 window 中存储了资源。待将来时机成熟能够从 window 这种全局净化比较严重的形式中切换进去,经营前端绝对于基座利用进一步独立。*/
interface IRequestGenerator {
 // 资源名称
 name: string;
 // 申请默认应用的 url 列表
 endpoints: (projectId: string, dependencies?: { [key: string]: any }) => string[];
 // 默认形式外的额定申请形式
 manual?: (projectId: string, dependencies?: { [key: string]: any }) => any;
 // 依据环境决定是否应用 manual 进行申请,将屏蔽 endpoints
 useManual?: boolean;
 // 前置依赖资源列表
 dependencies?: TResource[];
 // 资源在前端的长久化,不配置将默认同时存储在 sessionStorage 与 window 中
 persistence?: (resource: any) => void;
 // 申请后的手动操作
 afterRequest?: (resource: any) => void;
 // 是否缓存
 noCache?: boolean;
}

resourceService 加强

resourceService 是 GrowingIO SaaS 前端利用中进行数据获取的一个对立封装,还蕴含了 Redux 注入的过程,因而咱们在不影响原有性能的根底上,针对数据获取的每个裸露 API 新增了相似 @gio-bootstrap 中的 manual 字段,以进行对应资源在 OP 中的 GraphQL 或新 RESTful API 的申请。

3.2 集成

一般来说微前端实现架构是这样:

基座层作为主框架的核心成员,充当调度者的角色,由它来决定在不同的条件下激活不同的子利用。因而主框架的定位则仅仅是:导航路由 + 资源加载框架。

对应的 GrowingIO 理论状况是,Marketing 作为一个独立的子利用,须要嵌入 SaaS 和 OP 当中:

依照上图架构来做,则须要把 SaaS 和 OP 前端利用(暂不思考整个拆成微前端架构)当做基座利用。

因而在思考集成计划时,咱们做了一个集成计划比照:


在咱们开始思考集成的时候,两个基座利用的依赖状况举个例子就是:

SaaS     Dependencies     react: 16.8.3,    antd: ^2.13.3
OP       Dependencies     react: 16.8.6,    antd: ^3.20.5 
antd     PeerDependencies react: "~0.14.0 || >=15.0.0"

因为 Marketing 须要集成进两个平台,如果采纳构建时集成,则会造成相似 SaaS 利用编译后过大,无奈很好地兼容两个利用具备破坏性的依赖版本不统一等一系列令人头疼与困惑的问题。

同时,构建时集成所思考的依赖 hoist(具体概念能够参考 lerna hoist 进行了解)以及 npm 来解决依赖抵触的概念,与微前端所须要达成的「技术栈无关」、「利用自治」理念南辕北辙。

「技术栈无关」应该是咱们思考施行微前端所首先要达成的指标。

综合下面的几点思考,咱们抉择了采纳运行时 HTML Entry 的集成形式,只管就义了一部分打包加载优化,但换取了更加统一的开发体验以及无感独立公布的长处。

于是咱们的微前端集成计划能够大略形容成这样:


因为咱们目前我的项目及环境的特殊性,SaaS 和 OP 的入口文件会有所不同(别离打包各自所需模块代码),然而打包形式以及集成形式是一样的。

3.3 全局变量解决

微前端里微利用在很多状况下也会去批改全局变量,这时候就可能造成全局变量的命名抵触。可能的解决形式有如下:

综合思考子利用的独立性、环境的纯洁性,以及计划的执行老本,约定 namespace 计划绝对较好,缩小犯错老本同时又不过于简单。

因为历史遗留起因,SaaS 利用在 window 全局环境中存储或从新定义了很多货色,例如 window.fetch,window.can 办法以及大量的全局变量,并且在最后拆分经营前端仓库时候无奈一次性全副解决掉。

因而这一部分问题只能是分步骤迭代,一步步将全局变量、办法收束为 namespace 下的变量或者子利用下的模块导出。

3.4 款式笼罩

GrowingIO SaaS 利用最早是在 antd2.X 的根底上从新封装,并且始终没有追随社区降级;OP 利用应用了最新版本的 antd,这就使得 Marketing 在集成到 OP 上时会呈现 antd 类名的抵触与笼罩。

同时 OP 和 SaaS 在咱们本人的组件类名上也会有不同的设计语言,更进一步加剧了款式的抵触。

应答这样的款式抵触,其实只须要批改 webpack 打包时 style-loader 的默认行为就能够。

目前 style-loader 默认将款式插入 head 当中,那么咱们就在基座利用的子利用入口处自行制订插入 dom,将款式插入到本人的作用域下,随着利用的切换,顺带将款式卸载。

对于应用到的 styled-components 也利用库本人的 API 做同样解决。

不过就算这样解决了还是会偶然发现款式不协调的中央,这方面的坑还须要再踩一踩才会有更新的领会。

4. 结语

从一个巨石利用中拆分微前端不是一个能够欲速不达的事件,总有各种各样的问题会忽然呈现。

不同产品,不同我的项目,会遇到的问题不尽相同,但大体思路与解决办法基本一致,只有围绕最外围的「技术栈无关」,抉择一个适合我的项目的计划,而后一步步做到依赖、款式、全局变量等的隔离与独立,最终达到可能独立打包公布又不影响基座利用与兄弟微利用。

对于 GrowingIO

GrowingIO 是国内当先的数据驱动增长解决方案供应商。为产品、经营、市场、数据团队及管理者提供客户数据平台、获客剖析、产品剖析、智能经营等产品和咨询服务,帮忙企业在数据化降级的路上,晋升数据驱动能力,实现更好的增长。

点击「此处」获取 GrowingIO 15 天收费试用!

退出移动版