微前端大赏
序
什么是微前端?微前端解决了什么问题?
要回答这个问题,我们首先要解决的是:什么是“微”。大家可能已经听说过微服务的概念, 微服务是后端服务的一种架构模式,它想解决的问题是可用性问题、扩展性问题、耦合度问题,进而演变出“服务治理”,"服务发现"等等技术。例如:
- 通过熔断、限流等机制保证高可用;
- 微服务之间调用的负载均衡;
- 分布式事务(2PC、3PC、TCC、LCN等);
- 服务调用链跟踪
- 配置中心
- 服务自动发现等等。
1、微服务的“微”字在于 单一职责
一个微服务应该都是单一职责的,这才是“微”的体现,一个微服务解决一个业务问题(注意是一个业务问题而不是一个接口)。
2、还有一个面向服务
将自己的业务能力封装并对外提供服务,这是继承SOA的核心思想,一个微服务本身也可能使用到其它微服务的能力
这两个基础的能力构成了微服务整个的架构体系,是围绕服务、围绕一个个单一的职责体系的,它将一个、多个不同业务体系内的服务连接起来合并成一个大的业务模块,再分而治之,对每个服务做相应的技术、业务处理,合并成了一整个面向服务的业务。当服务发生故障,熔断机制产生作用,兜底服务马上启用,然后调用告警服务,将信息通知给通知服务,接着通知服务负责提醒对应的人员查看并解决问题。
这一系列的操作就是微服务的“微”字所要解决的问题,它把传统的大型项目拆分成各个不同的业务模块,再由各种一致性组件、可用性组件把它们组合起来使用。
“微”是分治的意思,那微前端是什么呢?
历史
后端jsp时代
JSP时代没有太多的悬念,我依稀还记得那个年代,当我clone下后端爸爸的代码,笨拙的在windows电脑上按照csdn的步骤安装java的jdk,打开百度搜索“JRE和JDK的区别是什么,我有没有装错”...一言难尽。总之那个年代,我们前端的代码大多数必须经过后端同学在jsp里面的标签处理才可以在线上使用这个时候拆分、分治的工作都集中在js,会分为很多套不同的js代码,在script中依次引入操作的。
这个阶段前端其实并不“微”,只是作为一个界面脚本标记存在的而已。
iframe时代
渐渐的ajax、jquery、require.js的出现打破了前端生态的模式,ajax使前后端分离,jquery使前端变得更加容易编写,而AMD的模块规范以及require.js的出现让前端从此变得不一样了,前端进入了模块化的时代。
require.js是遵循AMD协议的一个前端模块化库
最早的时候,所有Javascript代码都写在一个文件里面,只要加载这一个文件就够了。后来,代码越来越多,一个文件不够了,必须分成多个文件,依次加载。下面的网页代码,相信很多人都见过。
<script src="1.js"></script>
<script src="2.js"></script>
<script src="3.js"></script>
requireJS的写法:
模块代码:
// main.js
require(['moduleA', 'moduleB', 'moduleC',function (moduleA, moduleB, moduleC){
// some code here
});
requireJS可以通过我们现在熟悉的request(),类似的写法去引入一个模块,在这个时候,它的理念跟iframe相结合,就有了第一个“微前端”的架构模式,当然这个时候的微前端并不很“微”。
通过一张阿里云的控制台的图来解释这套架构的模式:
主应用负责框架、通信、路由、资源分配。
子应用负责实现业务。
两者之间通过一套特定的sdk进行交互。
已经非常接近微服务的整体概念了。 通过主框架解决共性问题,拆分各个不同的微模块、微应用解决各个单一职责的问题,这个时候每个应用是面向应用的,即应用本身只对应用本身负责,它有很多特性:
1、技术栈无关,遵循同一套通信机制即可
2、应用解偶,团队之间通过主框架基座进行交互
3、热更新插拔,不需要全部更新主框架,只需要更新对应的应用即可
4、可动态降级熔断
等等特性,可以说这一时期的前端已经进入了微前端时代。当然不是所有的应用都适用于这一个庞大的开发模式,毕竟阿里云几十上百个不同的应用模块是需要庞大的业务支撑的。
打包技术与SSR(服务端渲染)
然后gulp出现了,webpack出现了,angular、vue、react单页应用都出现了。
但问题来了,我们知道我们一个单页应用里资源是很重的。首页的加载速度需要很大的代价去优化它。这个时候iframe会带来比较严重的体验问题。
Single-spa出现了。
一个用于前端微服务化的JavaScript前端解决方案
同样的技术栈无关,在同一个页面中使用多种技术框架(React, Vue, AngularJS, Angular, Ember等任意技术框架),并且不需要刷新页面.
也同样无需重构现有代码,使用新的技术框架编写代码,现有项目中的代码无需重构.
更好的资源控制,每个独立模块的代码可做到按需加载,不浪费额外资源.
每个独立模块可独立运行. 大致是这样的:
让我们再去盗几张别人的图:(图片来自网络,侵权通删)
Loader:
Loader是核心模块的加载器,可以通过loader来进行子应用的加载,目前的微前端方案设计里面一般有两种模式。
第一种是非侵入式(iframe模式),通过加载对应子应用的 index.html 文件,再通过对首页html文件进行解析,获取到子应用的js文件和css文件,进行加载。
另一种是子应用打包成一个js文件,按照规范的导出格式,主应用只加载 index.js 文件。获取到对应的render和destroy方法。
External:
在SPA微前端中有一个需要解决的问题就是,子应用间的公共依赖,我们如何抽离项目间的公共依赖呢,由于我们将一个应用拆分成了多个子应用,那子应用之间的依赖如何复用。如果了解commonJS的同学应该知道,commonJS具备加载模块缓存能力,加载过的模块会将其缓存起来,那么是不是我们可以将子模块以commonJS的规范进行打包。在加载子模块时,提供全局的exports和require方法,将子应用导出的exports进行收集,在require时加载我们配置的external资源。
核心问题
通信,首先要解决的问题
消息总线概念
消息总线。简单理解就是一个消息收发中心,众多应用可以连接到总线上,应用可以往消息中心发送或接收信息(通过订阅监听或主动推拉)。比如:应用A发送一条消息到总线上,总线判断应该送给应用B,应用B可以接收到信息(应用B订阅或拉取到了应用A的消息),这样的话,消息总线就充当一个中间者的角色,使得应用A和应用B解偶了,很方便。
在前端可使用的技术大致有:
1、通过window交互
需要注意的是domain域名的设置,比较复杂,维护成本高,不可控性高。
2、通过socket,主应用和子应用连接socket,通过服务端实现通信,一般没有人这么用,比较复杂, 成本高。
3、通过url进行简单的交互,大多应用采用的是由路由参数进行交互的,实现简单且体验较好。
4、localstorage等存储媒介。
鉴权问题
微前端怎么样在各个模块之间统一权限体系?这个问题前端解决的难度不低,玩的不好容易崩溃。
一般情况下由后台爸爸,通过cookie识别,从后台接口带出对应的权限数据在前端进行二次判断。
污染问题
1、全局环境污染
2、事件污染
3、style污染
4、定时器污染
5、localstorage污染
解决全局环境污染和style污染
通常采用,快照模式和代理劫持,
在新的api中还可以采用shadowbox
Sandbox
有一个核心的模块是沙盒,由于多个子应用会反复的展示在同一个容器内,子应用中会造成对当前环境的副作用,例如:全局样式、全局变量、监听事件、定时器等。沙盒在这里主要是为运行中的程序提供隔离环境,避免应用之间相互影响。
在应用的运行环境中做资源隔离,监听应用的生命周期进行清理、加载操作。
小结
什么是微前端:
微前端(Micro-Frontends)是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。各个前端应用还可以独立运行、独立开发、独立部署。微前端不是单纯的前端框架或者工具,而是一套架构体系,这个概念最早在2016年底被提出,可以参考在Google上搜索Micro-Frontends, 排名靠前的https://micro-frontends.org的博客文章,提出了早期的微前端模型。
它能做什么:
1、拆分和细化
2、整合历史系统
3、独立构建发布
4、治理、熔断、降级
等
相关资源
https://www.jianshu.com/p/c0f4b837dbea
https://zhuanlan.zhihu.com/p/162726399
https://zhuanlan.zhihu.com/p/141530392
下期我们可以具体实践实践,自己动手搭建一个基于singleSpa的微前端框架,敬请期待