介绍
qiankun 是基于 single-spa 做了二次封装的微前端框架,通过解决了 single-spa 的一些弊端和不足,来帮助大家实现更简单、无痛的构建一个生产可用的微前端架构系统。
为什么不使用 single-spa 呢?
single-spa 就做了两件事,加载微应用(加载方法还是用户自己提供的)、维护微应用状态(初始化、挂载、卸载)。了解多了会发现 single-spa 虽好,但是却存在一些比较严重的问题
- 对微应用的侵入性太强 single-spa 采用 JS Entry 的方式接入微应用。微应用改造一般分为三步:
- 微应用路由改造,添加一个特定的前缀
- 微应用入口改造,挂载点变更和生命周期函数导出
- 打包工具配置更改 侵入性强其实说的就是第三点,具体操作过程如下:
-
- 使用 single-spa 接入微应用需要将微应用整个打包成一个 JS 文件,发布到静态资源服务器。
-
- 在主应用中配置该 JS 文件的地址告诉 single-spa 去这个地址加载微应用。 会产生什么问题呢?
-
- 就这第三点的改动就存在很大的问题了,将整个微应用打包成一个 JS 文件,常见的打包优化基本上都没了,比如:按需加载、首屏资源加载优化、css 独立打包等优化措施。
-
- 项目发布以后出现了 bug ,修复之后需要更新上线,为了清除浏览器缓存带来的影响,一般文件名会带上 chunkcontent,微应用发布之后文件名都会发生变化,这时候还需要更新主应用中微应用配置,然后重新编译主应用然后发布,这套操作过程比较繁琐,相当消耗时间。
-
样式隔离问题 single-spa 没有做这部分的工作。一个大型的系统会有很的微应用组成,怎么保证这些微应用之间的样式互不影响?
这时只能通过约定命名规范来实现,比如应用样式以自己的应用名称开头,以应用名构造一个独立的命名空间,这个方式新系统还好说,如果是一个已有的系统,这个改造工作量可不小。 -
JS 隔离 这部分工作 single-spa 也没有做。 JS 全局对象污染是一个很常见的现象,比如:微应用 A 在全局对象上添加了一个自己特有的属性,
window.A,这时候切换到微应用 B,这时候如何保证window对象是干净的呢? -
资源预加载 这部分的工作 single-spa 更没做了,毕竟将微应用整个打包成一个 js 文件。 现在有个需求,比如为了提高系统的用户体验,在第一个微应用挂载完成后,需要让浏览器在后台悄悄的加载其它微应用的静态资源,这个怎么实现呢?
-
应用间通信 这部分工作 single-spa 没做,它只在注册微应用时给微应用注入一些状态信息,后续就不管了,没有任何通信的手段,只能用户自己去实现
-
如何解决以上问题呢? 以上 5 个问题中第 2、3、5 还好说,可以通过一些方式来解决。 比如2采用命名空间的方式解决样式隔离问题,3通过备份全局对象,每次微应用切换时初始化全局对象的方式来解决 JS 隔离的问题。5通信问题可以通过传递一些通信方法,这点依赖了 JS 对象本身的特性(传递的是引用)来实现;
但是第一个和第四个就不好解决了,这是 JS Entry 方式带来的问题,要解决这个问题,难度相对就会大很多,工作量也会更大。况且这些通用的脏活累活就不应该由用户来解决,而是由框架来解决。
为什么使用qiankun?
qiankun 基于 single-spa 做了二次封装,很好的解决了上面提到的第一个和第四个问题。把这些通用的脏活累活交给qiankun框架去做。
-
HTML Entry
qiankun 通过 HTML Entry 的方式来解决 JS Entry 带来的问题,让你接入微应用像使用 iframe 一样简单。
-
样式隔离
qiankun 实现了两种样式隔离
- 严格的样式隔离模式,为每个微应用的容器包裹上一个
shadow dom节点,从而确保微应用的样式不会对全局造成影响 - 实验性的方式,通过动态改写
css选择器来实现,可以理解为css scoped的方式
- 严格的样式隔离模式,为每个微应用的容器包裹上一个
-
运行时沙箱
qiankun 的运行时沙箱分为
JS沙箱和样式沙箱JS 沙箱为每个微应用生成单独的window proxy对象,配合HTML Entry提供的 JS 脚本执行器 (execScripts) 来实现 JS 隔离;样式沙箱通过重写DOM操作方法,来劫持动态样式和 JS 脚本的添加,让样式和脚本添加到正确的地方,即主应用的插入到主应用模版内,微应用的插入到微应用模版,并且为劫持的动态样式做了scoped css的处理。 -
资源预加载
qiankun 实现预加载的思路有两种,一种是当主应用执行
start方法启动 qiankun 以后立即去预加载微应用的静态资源,另一种是在第一个微应用挂载以后预加载其它微应用的静态资源,这个是利用 single-spa 提供的single-spa:first-mount事件来实现的。 -
应用间通信
qiankun 通过发布订阅模式来实现应用间通信,状态由框架来统一维护,每个应用在初始化时由框架生成一套通信方法,应用通过这些方法来更改全局状态和注册回调函数,全局状态发生改变时触发各个应用注册的回调函数执行,将新旧状态传递到所有应用。
架构
起步
主应用使用create-react-app创建react项目:
npx create-react-app bigass-micro-app
然后安装micro-app组件:
npm i @micro-zoe/micro-app --save
创建完了后,先用 React Router 来搭建基座应用的路由系统:
const App = () => {
return (
<BrowserRouter>
<div>
<header className={styles.header}>
<Link to="/">基座 Home</Link>
<Link to="/about">基座 About</Link>
</header>
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/about" element={<About/>}/>
</Routes>
</div>
</BrowserRouter>
);
}