一、微前端
微前端(Micro-Frontends)是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。微前端不是单纯的前端框架或者工具,而是一套架构体系。
目前的微前端框架一般都具有以下三个特点:
- 技术栈无关:主框架不限制接入应用的技术栈,子应用具备完全自主权。
- 独立性强:独立开发、独立部署,子应用仓库独立。
- 状态隔离:运行时每个子应用之间状态隔离。
当然,微前端还存在许多的问题,比如应用间的 样式冲突、消息通信、脚本互斥、公共依赖加载等等。
二、应用隔离
应用隔离主要分两种情况:
- 一种是 主应用与微应用之间的隔离;
- 一种是 微应用与微应用之间的隔离;
应用间所隔离的主要是 Javascript 的沙箱隔离和 CSS 的样式隔离。
CSS 样式隔离
由于在微前端场景下,不同技术栈的子应用会被集成到同一个运行池中,所以我们必须在框架层确保各个子主应用之间不会出现样式互相干扰的问题。
常见的解决方案
- 严格的命名约定,例如 BEM;
- CSS Module;
- 各种 CSS-in-JS 库;
- shadow DOM;
除了在css文件上做隔离,qiankun 也提供了样式隔离的功能。具体的方式有两种:严格样式隔离和 scoped 样式隔离。
qiankun 样式隔离方案
严格样式隔离
严格样式隔离,默认情况下是关闭的。如果需要开启,必须显示配置。
严格样式隔离,是基于 Web Component 的 shadow Dom 实现的。通过 shadow Dom, 我们可以将一个隐藏的、独立的 dom 附加到一个另一个 dom 元素上,保证元素的私有化,不用担心与文档的其他部分发生冲突。
scoped 样式隔离
scoped 样式隔离,是基于属性选择器实现的。类似 div["data-qiankun=react"]
html entry 解析以后的 html 模板字符串,在添加到 container 指定的节点之前,会先包裹一层 div,并且为这个 div 节点添加 data-qian 属性,属性值为子应用的 name 属性;然后遍历 html 模板字符串中所有的 style 节点,依次为内部样式表中的样式添加div["data-qiankun=xxx"]
前缀。qiankun 中子应用的 name 属性值是唯一的,这样通过属性选择器的限制,就可实现样式隔离。
严格样式隔离和 scoped 样式隔离不能同时使用,当两者对应的配置项都为 true 时,严格样式隔离的优先级更高。
Javascript 沙箱隔离
通常,子应用在运行期间会有一些污染性的副作用产生,比如全局变量、全局事件、定时器、网络请求、localStorage、全局 Style 样式、全局 DOM 元素等。
为了保证应用能够稳定的运行且互不影响,需要提供安全的运行环境,能够有效地隔离、收集、清除应用在运行期间所产生的副作用,也就是沙箱的设计目标。
JS隔离的方式主要有两种,一种是快照拷贝的方式,一个是基于proxy的方式。
qiankun 优先使用 proxy沙盒,如果浏览器不支持 proxy ,那么再使用快照沙盒。
快照沙箱 - snapshotSandbox
在创建微应用的时候会实例化一个沙盒对象,它有两个方法,active是在激活微应用的时候执行,而inactive是在离开微应用的时候执行。
整体的思路是在激活微应用时将当前的window对象拷贝存起来,然后从modifyPropsMap中恢复这个微应用上次修改的属性到window中。在离开微应用时会与原有的window对象做对比,将有修改的属性保存起来,以便再次进入这个微应用时进行数据恢复,然后把有修改的属性值恢复到以前的状态。
代理沙箱 - proxySandbox
微应用中的script内容都会加with(global)来执行,这里global是全局对象,如果是proxy的隔离方式那么他就是下面新创建的proxy对象。
我们知道with可以改变里面代码的作用域,也就是我们的微应用全局对象会变成下面的这个proxy。
当设置属性的时候会设置到proxy对象里,在读取属性时先从proxy里找,没找到再从原始的window中找。
也就是你在微应用里修改全局对象的属性时不会在window中修改,而是在proxy对象中修改。因为不会破坏window对象,这样就会隔离各个应用之间的数据影响。
三、跨应用通信
微前端最常见的问题之一是如何让微应用之间能够相互通信。
一般而言,我们建议让微应用之间尽可能少地交流,因为这通常会重新引入我们最初试图避免的那种不适当的耦合代码。也就是说,通常我们只需要某种程度的跨应用通信即可。
常见的通信方式:
- 使用 自定义事件通信,是降低耦合的一种好方法;
- 可以考虑 React 或 Vue 应用中常见的 全局 state store 机制;
- 发布-订阅(pub/sub)模式的通信机制;
- 使用 地址栏作为通信机制;
qiankun - Actions 通信
qiankun 内部提供了 initGlobalState 方法用于注册 MicroAppStateActions 实例用于通信,该实例有三个方法,分别是:
- setGlobalState:设置 globalState - 设置新的值时,内部将执行 浅检查,如果检查到 globalState 发生改变则触发通知,通知到所有的 观察者 函数。
- onGlobalStateChange:注册 观察者 函数 - 响应 globalState 变化,在 globalState 发生改变时触发该 观察者 函数。
- offGlobalStateChange:取消 观察者 函数 - 该实例不再响应 globalState 变化。
Actions 通信方案是通过全局状态池和观察者函数进行应用间通信,该通信方式适合大部分的场景。
四、微前端框架
Single-spa
Single-spa 是最早的微前端框架,兼容多种前端技术栈。
概念:Single-spa 是一个将多个单页面应用聚合为一个整体应用的 JavaScript 微前端框架。
我们都知道 spa 应用 的理念是让独立的应用程序组成一个完整的页面,Single-spa 并不用依赖于单个框架和每个特性,而是你可以在不同的新框架随意使用。
简单来说就是一个聚合,使用这个库可以让你的应用可以 使用多个不同的技术栈(vue、react、angular等等)进行同步开发,最后使用一个公用的路由去实现完美的切换。
优点:
- 敏捷性 - 独立开发、独立部署,微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新;
- 技术栈无关,主框架不限制接入应用的技术栈,微应用具备完全自主权;
- 增量升级,在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略
- 更快交付客户价值,有助于持续集成、持续部署以及持续交付;
- 维护和 bugfix 非常简单,每个团队都熟悉所维护特定的区域;
缺点:
- 无通信机制
- 不支持 Javascript 沙箱
- 样式冲突
- 无法预加载
Qiankun
Qiankun 是一个基于 single-spa ,阿里系开源的微前端框架,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。
优点:
- 基于 single-spa 封装,提供了更加开箱即用的 API。
- 技术栈无关,任意技术栈的应用均可 使用/接入,不论是 React/Vue/Angular/JQuery 还是其他等框架。
- HTML Entry 接入方式,让你接入微应用像使用 iframe 一样简单。
- 样式隔离,确保微应用之间样式互相不干扰。
- JS 沙箱,确保微应用之间 全局变量/事件 不冲突。
- 资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度。
- umi 插件,提供了 @umijs/plugin-qiankun 供 umi 应用一键切换成微前端架构系统。
- 社区较为活跃,维护者也较多,有问题会及时得到响应;
缺点:
- 可能对一些 jQuery 老项目支持性不是特别好;
- 安全和性能可能会有影响,具体取决于项目;
- 对 eval 的争议,eval函数的安全和性能是有一些争议的
Web Component
基于 Web Component 原生组件进行渲染的微前端框架有京东推出的 Micro App。
优点:
- 简单上手
- 无关技术栈:任何框架皆可使用;
- 静态资源补全;
- JS沙箱;
- 样式隔离;
- Qiankun 微前端框架的优势他都有;
另外腾讯推出的无界,也是一款基于 Web Components + iframe 微前端框架,具备成本低、速度快、原生隔离、功能强等一系列优点。
五、微前端使用过程中出现的问题
1. 微应用之间、主微应用之间相互跳转
用子应用router的history跳转会带上子应用的base,导致路由跳转404。这就造成使用微应用 router 的方法无法跳回主应用,也就无法直接跳到其他微应用。
解决方法
- 通过window.history.pushState()方式跳转。
- 将主应用的路由实例通过 props 传递给子应用,子应用使用主应用的路由实例进行跳转。
- 通过浏览器的自定义事件(document.dispatchEvent)传递主应用的history。
2. antd组件库的全局样式污染
如果是子应用之间的antd版本不用,那么不会有影响,但是主应用和子应用之间的版本问题会出现样式冲突
解决方法
- 使用@ant-prefix 这个变量给 antd 添加 namespace 的效果
- 使用qiankun提供的 scoped 隔离样式功能,如果使用这个功能,需要对Modal做一些处理
参考链接:
微前端学习系列(三):qiankun
除了 Qiankun, 这些微前端框架或许更适合你「建议收藏」
30分钟快速掌握微前端qiankun的所有核心技术
深入解析微前端乾坤原理
qiankun微应用之间、主微应用之间相互跳转方式总结与实践
你可能不需要微前端
微前端在美团外卖的实践