qiankun
学习qiankun.js之前先来了解下
微前端
的概念
微前端
微前端(Micro-Frontends)是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。各个前端应用还可以独立运行、独立开发、独立部署
为什么要用微前端
假设公司做了两个web项目,两个项目最开始都是独立部署
后来,这两个web项目需要合并成一个web项目,通过一个共同主页的tab,来切换访问
当然可以采取iframe的方式,将两个web项目嵌入到主页中,但这种情况下,总会出现一段白屏加载时间,应该如何优化呢?
另外,如果这两个应用之间有复杂的数据共享,数据通信,应该如何实现呢?
所以微前端出现了,可以处理上述情况,同时,它还具备很多架构上的核心价值
微前端架构核心价值
技术栈无关
主框架不限制接入应用的技术栈,可以随意选择vue
,react
,angular
等框架接入其中
公司或多或少会存在一些历史项目,这些项目大多以采用较老的前端技术,这些系统需要结合到新框架中来使用还不能抛弃,对此我们也没有理由浪费时间和精力重写旧的逻辑。而微前端可以将这些系统进行整合,在基本不修改来逻辑的同时来同时兼容新老两套系统并行运行
独立开发、独立部署
微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新
增量升级
在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略
每个微应用之间状态隔离,运行时状态不共享
可以将巨石应用拆解成若干可以自治的松耦合微应用
为什么不用 iframe
来自微前端框架qiankun的解答:
为什么不用 iframe,这几乎是所有微前端方案第一个会被 challenge 的问题。但是大部分微前端方案又不约而同放弃了 iframe 方案,自然是有原因的,并不是为了 "炫技" 或者刻意追求 "特立独行"
如果不考虑体验问题,iframe 几乎是最完美的微前端解决方案了
iframe 最大的特性就是提供了浏览器原生的硬隔离方案,不论是样式隔离、js 隔离这类问题统统都能被完美解决。但他的最大问题也在于他的隔离性无法被突破,导致应用间上下文无法被共享,随之带来的开发体验、产品体验的问题
- url 不同步。浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用
- UI 不同步,DOM 结构不共享。想象一下屏幕右下角 1/4 的 iframe 里来一个带遮罩层的弹框,同时我们要求这个弹框要浏览器居中显示,还要浏览器 resize 时自动居中
- 全局上下文完全隔离,内存变量不共享。iframe 内外系统的通信、数据同步等需求,主应用的 cookie 要透传到根域名都不同的子应用中实现免登效果
- 慢。每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程
其中有的问题比较好解决(问题1),有的问题我们可以睁一只眼闭一只眼(问题4),但有的问题我们则很难解决(问题3)甚至无法解决(问题2),而这些无法解决的问题恰恰又会给产品带来非常严重的体验问题, 最终导致我们舍弃了 iframe 方案
iframe和微前端
以微前端著名框架single-spa举例
iframe的优点
- 完全隔离了css和js,避免了各个系统之间的样式和js污染
- 可以在子系统完全不修改的情况下嵌入进来
iframe的缺点
- 页面加载问题: 影响主页面加载,阻塞onload事件,本身加载也很慢,页面缓存过多会导致电脑卡顿
- 布局问题:iframe必须给一个指定的高度,否则会塌陷。解决办法:子系统实时计算高度并通过postMessage发送给主页面,主页面动态设置高度,修改子系统或者代理插入脚本。有些情况会出现多个滚动条,用户体验不佳
single-spa的优点
- 加载快,可以将所有系统共用的模块提取出来,实现按需加载,一次加载,其他的复用
- 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染
- http请求少,服务器压力小
single-spa的缺点
- css和js需要制定规范,进行隔离。否则容易造成全局污染,尤其是vue的全局组件,全局钩子
- 子系统需少量改动,是不影响子系统独立开发部署及其功能
single-spa概览
一个将多个单页面应用聚合为一个整体应用的微前端框架。其包括以下内容:
- Applications,每个应用程序本身就是一个完整的 SPA (某种程度上)。 每个应用程序都可以响应 url 路由事件,并且必须知道如何从 DOM 中初始化、挂载和卸载自己。 传统 SPA 应用程序和 Single SPA 应用程序的主要区别在于,它们必须能够与其他应用程序共存,而且它们没有各自的 html 页面
- 一个 single-spa-config配置, 这是html页面和向Single SPA注册应用程序的JavaScript。每个应用程序都注册了三件东西
- A name
- A function (加载应用程序的代码)
- A function (确定应用程序何时处于活动状态/非活动状态)
single-spa原理
我们在使用诸如react
或vue
这些框架开发普通的单页应用时,会按照功能的封装或者样式的复用把页面拆分成很多的组件,组件可以拼装成一个个单独的页面,并且通过路由控制监听地址栏变化,动态加载不同的页面和组件,这就是单页应用运行的简单原理 single-spa
原理也大同小异,也可以类比的理解为一种模块化开发的单页应用,只不过在单项目的单页应用中划分的模块是各种组件,而在微前端中各个模块就是各个独立的子应用,用户访问基座主应用时会按照配置注册各子应用,并且根据路由来确定分发到哪个子应用来加载,对应子应用加载之后会再根据路由分发到对应的子应用里的页面,这就是single-spa
的路由加载原理先路由分发应用,再应用分发路由
为什么使用qiankun
single-spa
微前端框架主要存在下方问题
- 样式隔离 single-spa没有做这部分的工作。一个大型的系统会有很多的微应用组成,怎么保证这些微应用之间的样式互不影响?微应用和主应用之间的样式互不影响?这时只能通过约定命名规范来实现,比如应用样式以自己的应用名称开头,以应用名构造一个独立的命名空间,这个方式新系统还好说,如果是一个已有的系统,这个改造工作量可不小
- JS 沙箱 single-spa没有做这部分的工作。 JS 全局对象污染是一个很常见的现象,比如:微应用 A 在全局对象上添加了一个自己特有的属性,
window.A
,这时候切换到微应用B,这时候如何保证window
对象是干净的呢? - 资源预加载 single-spa没有做这部分的工作。微应用会整个打包成一个 js 文件,现在有个需求,比如为了提高系统的用户体验,在第一个微应用挂载完成后,需要让浏览器在后台悄悄的加载其它微应用的静态资源,这个怎么实现呢?
- 应用间通信 这部分工作 single-spa 没做,它只在注册微应用时给微应用注入一些状态信息,后续就不管了,没有任何通信的手段,只能用户自己去实现
qiankun完成了这些工作
- 📦 基于 single-spa 封装,提供了更加开箱即用的 API
- 📱 技术栈无关,任意技术栈的应用均可 使用/接入,不论是 React/Vue/Angular/JQuery 还是其他等框架
- 💪 HTML Entry 接入方式,让你接入微应用像使用 iframe 一样简单
- 🛡 样式隔离,确保微应用之间样式互相不干扰。
- 🧳 JS 沙箱,确保微应用之间 全局变量/事件 不冲突。
- ⚡️ 资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度
- 🔌 umi 插件,提供了 @umijs/plugin-qiankun 供 umi 应用一键切换成微前端架构系统
qiankun 的核心设计理念
🥄 简单
由于主应用微应用都能做到技术栈无关,qiankun 对于用户而言只是一个类似 jQuery 的库,你需要调用几个 qiankun 的 API 即可完成应用的微前端改造。同时由于 qiankun 的 HTML entry 及沙箱的设计,使得微应用的接入像使用 iframe 一样简单
🍡 解耦/技术栈无关
微前端的核心目标是将巨石应用拆解成若干可以自治的松耦合微应用,而 qiankun 的诸多设计均是秉持这一原则,如 HTML entry、沙箱、应用间通信等。这样才能确保微应用真正具备独立开发、独立运行的能力