什么是微前端
微前端为什么叫做微前端?主要是相对服务端的微服务来说的,微服务是将大型应用拆分为更小的独立服务,而微前端,可以理解为可以被动态加载的前端模块资源。
微前端理念
- 高隔离性
隔离性是微前端最重要的特性之一,良好的隔离性可以让子应用与子应用之间、子应用与基座之间互不干扰,子应用只运行在自己的沙箱里。
- 低耦合度
微前端要求各个子应用之间耦合度很低,往往框架的「隔离性」做得越好,子应用间通信的限制就越多,通信成本就越高。如果应用之间耦合度高,则会产生频繁的通信操作,从而增加使用成本。所以微前端架构强制要求应用之间低耦合。如果应用因业务原因耦合度不得不变高,则应从业务拆分的角度考虑是否将两个子应用整合成为一个。
- 高扩展性
微前端框架是一个非常灵活的框架,允许框架内的子应用进行任意形式的扩展,不限制基座和子应用依赖,基座和子应用可以基于业务和技术上来扩展功能,例如可以把基座扩展成一个云平台架子,或者改造成一个应用编排器。
- 低侵入性
从微前端框架层来说,无论是对基座还是子应用,都需要保证尽可能低的侵入。能只侵入依赖的就不侵入代码,能只侵入 10 行代码的坚决不侵入 20 行。过高的侵入性会增加使用成本,降低扩展性和协作效率。侵入性和扩展性共同决定了微前端框架的易用性。
为什么要做微前端?或者说微前端是要解决什么问题?
Single-spa 最初要解决的问题是,在老项目中使用新的前端技术栈。 Qiankun 所声明的微前端想要解决的另一个主要问题是,巨石工程的维护困难和协作开发困难。而这两个目标基本上是一致的,主要目标是拆分,对大型工程进行解耦。
微前端首先解决的,是如何解构巨石应用 ,从而解决巨石应用随着技术更迭、产品升级、人员流动带来的工程上的问题。解构之后还需要再重组,重组的过程中我们就会碰到各种 隔离性、依赖去重、通信、应用编排 等问题。
为什么不是Iframe?
iframe 最大的特性就是提供了浏览器原生的硬隔离方案,不论是样式隔离、js 隔离这类问题统统都能被完美解决。但他的最大问题也在于他的隔离性无法被突破,导致应用间上下文无法被共享,随之带来的开发体验、产品体验的问题。
- url 不同步。浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用。
- UI 不同步,DOM 结构不共享。想象一下屏幕右下角 1/4 的 iframe 里来一个带遮罩层的弹框,同时我们要求这个弹框要浏览器居中显示,还要浏览器 resize 时自动居中..
- 全局上下文完全隔离,内存变量不共享。iframe 内外系统的通信、数据同步等需求,主应用的 cookie 要透传到根域名都不同的子应用中实现免登效果。
- 慢。每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程。
Single-Spa怎么做的?
single-spa 是一个用于前端微服务化的 JavaScript 前端解决方案 (本身没有处理样式隔离, js 执行隔离) 实现了路由劫持和应用加载。 single-spa 仅仅是一个子应用生命周期的调度者。 single-spa 为应用定义了 boostrap, load, mount, unmount 四个生命周期回调:
当 window.location.href 匹配到 url 时,开始走对应子 App 的这一套生命周期嘛。所以,single-spa 还要监听 url 的变化,然后执行子 app 的生命周期流程。 到此,我们就有了 single-spa 的大致框架了,无非就两件事:
- 实现一套生命周期,在 load 时加载子 app,由开发者自己玩,别的生命周期里要干嘛的,还是由开发者造的子应用自己玩
- 监听 url 的变化,url 变化时,会使得某个子 app 变成 active 状态,然后走整套生命周期
站在single-spa的肩膀上,qiankun做了什么?
qiankun 并不是新的微前端框架,它只是解决了 single-spa 没解决的一些问题,是更完善的基于 single-spa 的微前端方案。
加载子应用的资源的方式
它会加载入口 html,解析出 scripts、styles 的部分,单独去加载,而其余的部分,会做一些转换之后放到 dom 里。实现了Html Entry
JS、CSS沙箱
JS沙箱
qiankun 实现 window 隔离有三种思路:
- 快照,加载子应用前记录下 window 的属性,卸载之后恢复到之前的快照
- diff,加载子应用之后记录对 window 属性的增删改,卸载之后恢复回去
- Proxy,创建一个代理对象,每个子应用访问到的都是这个代理对象。
- 单例模式
- 通过proxy劫持window,子应用的每个操作都记录下来,在子应用卸载之后,反向操作,还原成原本的window
- 多例模式
- 通过proxy劫持window,子应用的每个操作都记录下来,在子应用卸载之后,反向操作,还原成原本的window
- 单例模式
样式沙箱
通过增强多例模式下的 createElement 方法,负责创建元素并劫持 script、link、style 三个标签的创建动作。
增强 appendChild、insertBefore 方法,负责添加元素,并劫持 script、link、style 三个标签的添加动作,根据是否是主应用调用决定标签是插入到主应用还是微应用,并且将 proxy 对象传递给微应用,作为其全局对象,以达到 JS 隔离的目的。
样式沙箱实际做的事情其实很简单,就是将动态添加的 script、link、style 这三个元素插入到对的位置,属于主应用的插入主应用,属于微应用的插入到对应的微应用中,方便微应用卸载的时候一起删除, 当然样式沙箱还额外做了两件事:
- 在卸载之前为动态添加样式做缓存,在微应用重新挂载时再插入到微应用内;
- 将 proxy 对象传递给 execScripts 函数,将其设置为微应用的执行上下文;
样式隔离方案
- Shadow DOM:Qiankun 利用 Shadow DOM 技术实现了样式隔离。Shadow DOM 允许将 DOM 和 CSS 封装在一个独立的作用域中,从而避免了全局样式的污染。当使用 Shadow DOM 作为样式隔离策略时,Qiankun 会将子应用的 DOM 结构放入一个封闭的 Shadow Root 中,这样子应用的样式将不会影响到其他子应用和主应用。
- 沙盒化 CSS:Qiankun 还支持沙盒化 CSS 作为另一种样式隔离策略。沙盒化 CSS 的主要思路是通过修改 CSS 选择器,在子应用的样式规则前加上一个特定的前缀。这个前缀是一个唯一的标识,用于确保子应用的样式规则仅对其自身生效,而不会影响到其他子应用或主应用。当子应用被激活时,Qiankun 会动态地为子应用的 CSS 规则添加这个前缀。