浅谈微前端在滴滴车服中的应用实践

  1. 云栖社区>
  2. 博客>
  3. 正文

浅谈微前端在滴滴车服中的应用实践

云栖号资讯小哥 2020-05-28 15:39:29 浏览480
展开阅读全文

云栖号资讯:【点击查看更多行业资讯
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!


导读:如果盘点2019年最火的前端技术,那么微前端肯定占有一席之地。但是大部分人对于微前端这个架构新贵的了解还是处于懵懵懂懂的状态,本文将会详细介绍微前端的前生今世,带大家了解微前端架构是如何一步步从实际需求中演化而来,以及小桔车服基于微前端所提炼的一套中后台体系建设实践。

1. 什么是微前端

微前端这个概念最初是对应于微服务这个概念提出的,微服务的核心思想就是将一个大的单体应用基于业务能力拆分为一组小的服务,每个服务都是独立的进程且能独立部署,无需统一的技术栈进行集中化管理,只需要进行轻量级的通信就可以完成业务诉求。微前端就是这样一种类似于微服务的架构模式,它将微服务的核心思想应用于浏览器端,即将单个复杂(大规模)的前端应用转换成多个小型前端应用,每个小型前端应用都与技术栈无关,可以独立开发、独立部署,当然拆分还需要一套成熟的通信机制串联起所有的应用,既能保证应用自治又能保证应用联系,以更好的协同度共同提升开发效率。

总结来说,微前端就是在前端一体化的大背景下,利用技术手段达到业务层应用聚合、技术层应用自治的工程架构方案,实现一个功能丰富且强大的前端应用。

2. 为什么需要微前端

是不是还是感觉有些朦朦胧胧,没关系,基于任何技术都需要依托于业务的原则,我们举一个浅显易懂的例子来帮助你理解一下前端为什么需要微前端架构:

在很多年前,小红和小兰决定创业,开一家网上商城,两个人一拍即合,快速设计出原型1.0版本,使用最原始的Jquery以及Html产出了一套面向用户展现的购物网站以及面向运营展现的管理后台:

1

为了方便理解,我们先不管前台的购物系统,只专注后台管理系统,因为项目前期只需要满足最基本的购物需求,所以当时所有的管理需求都集中在一个管理系统,代码收拢在一个仓库,具体架构如下:

2

小红和小兰迅速将1.0版本的网上商城推向市场,由于抢占了先机,大家很喜欢这种足不出门就可以购物的感觉,两人迅速赚到了一笔钱。

后来随着业务越做越大,商品管理逐渐分成了精选商品管理和普通商品管理,库存管理分成了合作商库存和自营库存,订单管理和用户管理也愈发的庞大,成了这个样子:

3

可以明显看出,随着业务的繁杂,每个模块的管理变的愈发臃肿,所有团队都在一个系统中维护不同的功能变的越来越麻烦,因此小红和小兰决定了上线2.0版本,将整个后台管理系统解耦拆分,由不同的团队维护不同的模块,A团队只负责商品管理这块,B团队只负责库存管理这块,其余模块也类似,这样大家各自部署,各自开发,互不干涉。

在2.0模式下,当业务越做越大,小红和小兰决定再成立营销和渠道两个团队,分别开展一些营销活动和渠道活动时,后台管理系统也需要增加一个渠道管理和营销管理模块,沿用上面解耦的逻辑,这俩团队再新建一个渠道系统和营销系统分别管理,大家各自产出自己的代码,各自维护各自的系统,扩展性大大增强。

同时随着前端技术的发展,Jquery以及Html的框架逐步落后, Angular、 React、Vue等单页面应用异军突起成为主力,各个团队都逐渐开始重构各自的系统,商品管理系统用了React框架,订单管理系统用了Vue框架等等,大家各自朝着自己感兴趣的框架上发力,各自为政的状态让大家都互不干扰,这样做就满足了最开始代码解耦的需求。

4

2.0模式的一切看起来都挺完美,但是真的很完美么,很快问题就出来了:

首先,苦了真正使用后台系统的运营同学和产品同学,这些同学想要使用后台系统的各种功能,日常操作就变成了不断的切换、切换、再切换系统。例如他们想要上架一个新的商品,需要先去商品管理系统配置一系列信息,再去库存管理系统查询相应的商品库存,最后再去营销系统配置这个商品的营销活动,整个过程需要不断的切换系统,运营效率大大降低。

然后,开发效率也大大降低,比如所有的系统都是基于同一套登录权限模块,但由于部署在不同的域名环境,每个系统都重复开发一遍,类似的网关模块、日志模块等等也是如此。而且不同系统之间存在大量的耦合功能,单独的代码环境并不能复用一些其他系统已有的代码,就会造成各种重复造轮子的行为。

有什么样的方式既可以让各个系统既能单独开发部署,各自选择技术栈,又能紧密联系聚合成一个系统供运营同学和产品同学使用呢,微前端的架构思想应运而生。

微前端的核心思想就是将一个完整的前端应用分解成一些更小的、微粒化的、能够独立开发测试部署的子应用,子应用之间通过弱通信机制互相联系,在用户看来仍然是内聚的单个产品。

那么整个电子商城的后台管理系统可以使用微前端的思想重新架构一番,也就是3.0模式:

5

这样,从产品维度来看,所有的系统都内聚在一个基系统中,用户只需要一次登录就可以不刷新的切换各个系统,所有功能都内聚在一起,有效提高了运营效率;从技术维度来看,各个系统并不需要进行技术栈的重构,依然可以沿用原有技术栈,每个团队依然各自维护各自的代码库,独立部署独立上线,且可以共用一些通用的能力如登录、鉴权、打点、日志分析等,即保证了遗留系统的迁移,又聚合了前端应用,完美解决应用臃肿情况下如何各自治理的问题。

相信通过上面例子,在一个虚拟电子商务后台系统架构的逐渐演化中,你应该了解了前端为什么需要微前端架构,总结来说,微前端具备下面优势:

  • 灵活聚合业务成系统:产品功能耦合,面向用户时多个不同的业务功能耦合成一个业务系统。
  • 兼容多技术栈:无论技术栈是Vue,还是React,或者Angular,都可以完美的在一个系统中运行。
  • 独立开发部署:子应用之间仓库独立,可以各自开发,部署后容器层会完成自动的同步更新。
  • 依赖资源复用:抽离不同应用中所依赖的公共资源,一次性加载多方复用,从而提升性能。

3. 微前端的技术选型

前面介绍了为什么需要微前端架构,那么接下来介绍一下技术选型,首先需要明确一个概念,微前端是一种架构思想,并不具体指某个技术,它是当前端业务在发展到一定规模之后,一种用来分解复杂度的架构模式,具体可以考虑以下几种选型:

路由式分发

这是最古老的微前端技术实现方式之一,也是最容易的实现方式,通过HTTP的反向代理功能,经过路由判断将请求转发到响应的服务上去,优点就是几乎不需要做任何改造,配置一下nginx服务即可,缺点也很明显,完全没有性能优化,切换系统仍需刷新页面重新加载资源文件,只是从表层将不同应用拼凑到一起。

iframe容器

这是最古老的微前端技术实现方式之二,虽然简单但是确实有效,iframe一直是浏览器规范之一,除了某些化石级别的版本,几乎所有的浏览器都可以完全兼容。Iframe的页面与父页面分开,完全不受父页面css或者全局的javascript 影响,在一定程度上类似于“沙箱隔离”,但一个系统如果加载过多的iframe体验会很不友好,重复加载相同的依赖,影响网页加载速度。

微件式服务

微件化是指某个应用由开发人员提前将完整的、闭环的所有功能提前编译好,其他应用可以直接嵌入到网页中而不需要做任何的修改或者编译就可以直接运行展示。早期很多应用的引入都是这样做的,将本身应用封装成sdk包,使用者远程加载javascipt代码就能生成对应的组件嵌入到页面,后续随着npm的流行,逐渐发展成将应用以NPM包的形式发布源码,这样做的优势是发布灵活单独部署打包,缺点就是如果引入多个应用微件,可能存在互相干扰的问题,且应用间通信困难,对遗留应用需要做过多改造。

Web Components

Web Components是一个Web组件标准,它提供了浏览器底层的支持,不依赖各种框架的支持和webpack的编译,让人们也可以使用组件。Web Components通过一种标准化的非侵入的方式封装一个组件,每个组件能组织好它自身的HTML、CSS、JavaScript,并且不会干扰页面上的其他代码。使用方式与frame比较类似,组件拥有自己独立的 Scripts 和 Styles,以及对应的用于单独部署组件的域名,内部资源高内聚,作用域独立,加载由自身控制。看上去Web Components确实是一种比较好的微前端架构选型,但遗憾的是目前浏览器的支持程度尚不完善,在Safari、ie和火狐上支持并不理想,如果不考虑多浏览器的兼容,Web Components是一个不错的选择。

自制框架的微应用式服务

微应用式服务是指系统在开始都是以单一微小应用的形式存在,只有当运行时才由系统框架将这些应用加载合并,组合成一个完整系统。无论是基于虚拟树的react和vue框架,还是基于Web Components的Angular框架,所有框架的原型都离不开在html里加载元素,那么,我们只需要提前将单个系统打包编译成一个微应用,在页面合适的地方引入或者创建 DOM,这样当用户操作时触发应用的启动,并能在用户切换时卸载应用,所以这个微应用式服务的核心在于加载器的设计,既能实时加载切换不同应用,又能有效隔离应用防止互相干扰。Single-SPA是现在较为成熟的一个开源框架,可以实现在一个页面将多个不同的框架整合,甚至在切换的时候都不需要刷新页面,已支持 React、Vue、Angular 1、Angular 2、Ember 等等,如果想要简单的将工程微应用化,可以考虑使用这个框架。

当然,没有银弹可言,微前端并不是万金油,无论是哪一种实现方式,我们在考虑采用微前端架构之前,除了考虑它带来的好处,还要考量存在的大量风险和技术挑战,例如:

  • 使用之后如何区分开发环境和线上环境
  • 如何隔离 JS,避免 CSS 冲突
  • 应用间共享公共资源的机制
  • 第三方模块重叠
  • 处理数据获取并考虑用户的初始化加载状态
  • 权限如何接入
  • 如何减少初始 Loading 时间
  • 如何有效测试

所以,微前端与微服务一样,要真正进入实践,还需要做好一系列的技术储备。目前小桔车服结合实际业务形态,构建出一套全链路的的产品接入平台,解决了上述微前端中的技术卡点,为中后台体系建设提供了一套通用的解决方案。

4. 微前端在车服中的实践

先介绍下背景,车服租车业务线有非常高的业务复杂度,并经历了多次商业模式探索整合优化。在业务不断探索调整的过程中,租车商用和MIS系统形成了多个系统分治的局面,同时林林总总的诸如采购、营销、企业车队等独立系统也都因业务侧的诉求纷纷进行了新建,且有更多的新的独立系统在业务侧筹划构建的路上,这极大加剧了开发和维护这些中后台系统的成本。

为此,团队决定以整合当前集团和车服环境内LASS能力为基点,提供一套微前端的架构体系,从页面资源到微应用再到系统级的搭建和管理的统一能力,即Midway平台。

6

如上,Midway平台以微前端架构思想为基础,采用基座模式,提供一个主应用即基座作为系统的统一入口,管理子应用的生命周期以及应用间的通信,提供核心部分的业务功能如用户登录、统一鉴权、导航菜单、路由管理等功能,并将对应的请求指向对应的服务,子应用则是具体负责子模块的业务实现,无视技术栈,由各个团队独立开发部署。

Midway 底层以single-spa为基础,隔离子应用间的样式与作用域,通过应用注册、钩子引用等方法,对接入的应用进行完整的生命周期控制,同时提供了应用通讯、公共资源加载、应用按需加载、应用预加载、日志监控等多种底层功能,下面捡重点介绍一下:

应用注册

Midway 调用 single-spa 的 registerApplication方法注册微应用,支持传入异步函数 loadApp(返回 Promise 即可)及非函数类型值。如果是非函数类型,它会主动转换,在钩子返回前后及返回的钩子做了一些功能建设:

  • 新增 beforeUnmount、 afterUnmount、 beforeMount、 afterMount、 beforeLoad 钩子,方便在应用mount及load前后做一些处理工作。
  • 根据单页面构架器(飞马)获取静态资源,为飞马页面entry.js添加钩子及相关配置等。
  • 根据应用注册时传入的entry类型,分析处理获取当前应用的 html、scripts、styles、id(飞马页面) 等信息。
  • 根据 entry 配置,异步拉取相关静态资源,最终返回用来渲染的模板代码 template和 execScripts,用来执行 entry.js 获取钩子的方法。
  • 为当前应用创建沙箱环境(proxy),并在沙箱环境下执行 execScripts获取 entry.js 的钩子函数,最后 loadApp返回加工后的钩子函数。

应用预加载

应用的预加载方案我们之前讨论了不少时间,考虑到以后管理的微应用规模及性能影响,我们决定采用预加载配置方案。需要手动配置哪些应用需要提前加载静态资源,主要我们来详细说说背后的处理及思考:

过滤已经 mount 过的应用,已经 mount 过的应用资源肯定加载过了,所以不需要预加载了。

注册的微应用中的第一个应用 mount 后,才会对配置的其它预加载应用做预加载,single-spa 做了一些自定义事件,其中有一个就是在第一个微应用 mount 后触发的事件 single-spa:first-mount。因此,我们监听此事件,当第一个微应用 mount 后,我们就可以开始预加载任务了。

利用 window.requestIdleCallback API 在浏览器空闲时间预加载应用资源, 在 mobile 设备和弱网环境下,我们不会开启预加载任务。在不支持 window.requestIdleCallbackAPI 的浏览器里,我们使用 setTimeout 来模拟。

JS沙箱

“沙箱”这个词听起来高大上,但是其实我们很早就已经接触过了。具体的概念及细节这里不再赘述,大家可以自行搜索。简单的说,当你要解析或执行不可信的 js 的时候,当你要隔离被执行代码的执行环境的时候,当你要对执行代码中可访问对象进行限制的时候,沙箱就派上用场了。

样式隔离

劫持 HTMLHeadElement.prototype.appendChild 方法,接管 appendChild,在应用 mount、unmount 时做样式快照生成与恢复操作。后续考虑使用 shadow dom 方案做样式隔离,更彻底安全。

资源缓存

本地缓存资源,能有效减少资源请求加载的时间,加快应用渲染,减少页面空白时间。对比过浏览支持的各种本地数据存储方案,最终选择 IndexedDB 来做存储静态资源解决方案,为什么选择它,这里就不做过多赘述了。详细处理有以下几点:

  • 使用 Dexie.js 来管理 IndexedDB 数据库
  • 设置缓存周期为 7 天,过期资源会被清理
  • 核心 API 有 cacheAssets、 getCacheAssets、 clearExpiredAssets

5. 总结

总结来说,Midway 整体设计理念以高内聚、低耦合为标准,秉承微前端的理念,实现了一套前台渲染、后台管理的平台化服务体系,用户不必再去关注应用聚合时的技术细节,开箱即可用。

目前,租车业务线的多系统分治的情况已开始使用Midway平台着手治理,各问题域模块也在相继按照微应用的粒度进行拆分以实现多系统间复用,同时新立系统也已有多个接入,极大降低了系统搭建的门槛和业务模块开发的重复性。我们未来围绕Midway微前端核心能力建设的同时,将持续把工程化、安全监控、性能体验、数据可视化等方向的能力逐步融入到Midway中,力求使该平台成为基于微前端的中后台系统一站式搭建解决方案的最佳实践。

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/live

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-05-28
本文作者:滴滴技术
本文来自:“掘金”,了解相关信息可以关注“掘金”

网友评论

登录后评论
0/500
评论
云栖号资讯小哥
+ 关注