# 什么是微前端
微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。
微前端架构特点:
- 无关技术栈
- 开发、仓库、部署独立
- 允许增量升级、渐进式重构
- 微应用之间状态隔离,运行时状态不共享
# 关于 Iframe
如果不考虑体验问题,iframe 几乎是最完美的微前端解决方案了。
iframe 最大的特性就是提供了浏览器原生的硬隔离方案,不论是样式隔离、js 隔离这类问题统统都能被完美解决。但他的最大问题也在于他的隔离性无法被突破,导致应用间上下文无法被共享,随之带来的开发体验、产品体验的问题。
- url 不同步,浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用。
- dom 结构不共享,想象一下屏幕右下角 1/4 的 iframe 里来一个带遮罩层的弹框,同时我们要求这个弹框要浏览器居中显示,还要浏览器 resize 时自动居中..
- 全局上下文完全隔离,内存变量不共享。iframe 内外系统的通信、数据同步等需求,主应用的 cookie 要透传到根域名都不同的子应用中实现免登效果。
- 慢。每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程。
更多关于微前端的相关介绍,推荐大家可以去看这几篇文章:
# 各大框架
# QianKun
QianKun (opens new window) 基于 single-spa ,阿里系开源的微前端框架,应该也是大家接触最多的了,社区比较活跃
,这点比较重要。
# 实现方案
single-spa是基于js-entry方案,而qiankun 是基于html-entry 及沙箱设计,使得微应用的接入像使用 iframe 一样简单。
主应用监听路由,加载对应子应用的html,挂载到主应用的元素内,然后解析子应用的html,从中分析出css、js再去沙盒化后加载执行,最终将子应用的内容渲染出来。
qiankun实现样式隔离有两种模式可供开发者选择:
- trictStyleIsolation:这种模式下 qiankun 会为每个微应用的容器包裹上一个 shadow dom 节点,从而确保微应用的样式不会对全局造成影响
- experimentalStyleIsolation: 当 experimentalStyleIsolation 被设置为 true 时,qiankun 会改写子应用所添加的样式,会为所有样式规则增加一个特殊的选择器规则,来限定其影响范围
qiankun实现js隔离,采用了两种沙箱,分别为基于Proxy实现的沙箱和快照沙箱,当浏览器不支持Proxy会降级为快照沙箱
Proxy沙箱机制:
// 伪代码
class ProxySandbox {
constructor() {
const rawWindow = window;
const fakeWindow = {}
const proxy = new Proxy(fakeWindow, {
set(target, p, value) {
target[p] = value;
return true
},
get(target, p) {
return target[p] || rawWindow[p];
}
});
this.proxy = proxy
}
}
let sandbox1 = new ProxySandbox();
let sandbox2 = new ProxySandbox();
window.a = 1;
// 伪代码
((window) => {
window.a = 'hello';
console.log(window.a) // hello
})(sandbox1.proxy);
((window) => {
window.a = 'world';
console.log(window.a) // world
})(sandbox2.proxy);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
快照沙箱:
// 伪代码
class SnapshotSandbox {
constructor() {
this.proxy = window;
this.modifyPropsMap = {}; // 修改了那些属性
this.active(); // 调用active保存主应用window快照
}
/**1. 初始化时,在子应用即将mount前,先调用active,保存当前主应用的window快照*/
active() {
this.windowSnapshot = {}; // window对象的快照
for (const prop in window) {
if (window.hasOwnProperty(prop)) {
// 将window上的属性进行拍照
this.windowSnapshot[prop] = window[prop];
}
}
Object.keys(this.modifyPropsMap).forEach(p => {
window[p] = this.modifyPropsMap[p];
});
}
/**
* 子应用卸载时,遍历当前子应用的window属性,和主应用的window快照做对比
* 如果不一致,做两步操作
* 1. 保存 不一致的window属性,
* 2. 还原window
*/
inactive() {
for (const prop in window) { // diff 差异
if (window.hasOwnProperty(prop)) {
// 将上次拍照的结果和本次window属性做对比
if (window[prop] !== this.windowSnapshot[prop]) {
// 保存修改后的结果
this.modifyPropsMap[prop] = window[prop];
// 还原window
window[prop] = this.windowSnapshot[prop];
}
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 优点
html entry的接入方式,不需要自己写load方法,而是直接写子应用的访问链接就可以。
提供js沙箱
提供样式隔离,两种方式可选 资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度。
社区活跃 umi 插件,提供了 @umijs/plugin-qiankun 供 umi 应用一键切换成微前端架构系统,除了最后一点拓展以外,微前端想要达到的效果都已经达到。
应用间通信简单,全局注入路由保持,浏览器刷新、前进、后退,都可以作用到子应用
路由保持,浏览器刷新、前进、后退,都可以作用到子应用
# 缺点
改造成本较大,从 webpack、代码、路由等等都要做一系列的适配
对 eval 的争议,eval函数的安全和性能是有一些争议的:MDN的eval介绍;
无法同时激活多个子应用,也不支持子应用保活
无法支持 vite 等 ESM 脚本运行,3.0 版本预计支持
# wujie
无界是腾讯推出的一款微前端解决方式。它是一种基于 Web Components + iframe 的全新微前端方案,继承iframe的优点,补足 iframe 的缺点,让 iframe 焕发新生。
# 实现方案
wujie跟qiankun一样,都是基于html entry加载的,但他们解析html的过程是不一样的。 qiankun是直接解析并执行js、css、html的,而wujie则是先解析html,提取出script脚本放入空的iframe中,提取出css、html放入到web components中,具体来说:
- 解析入口 HTML ,分别得到script、css、模版html
- 创建一个纯净的 iframe,为了实现应用间(iframe 间)通讯,无界子应用 iframe 的 url 会设置为主应用的域名(同域),因此 iframe 的 location.href 并不是子应用的 url。创建好后停止加载iframe
- iframe内插入js,将抽离出来的script脚本,插到iframe中去,在iframe中执行子应用的js
- 创建web component,id为子应用id,将抽离出来的html插入
- 由于iframe内的js有可能操作dom,但是iframe内没有dom,随意wujie框架内对iframe拦截document对象,统一将dom指向shadowRoot,此时比如新建元素、弹窗或者冒泡组件就可以正常约束在shadowRoot内部
# 优点
接入简单,可以以组件的方式引入子应用
纯净无污染
- 无界利用iframe和webcomponent来搭建天然的js隔离沙箱和css隔离沙箱
- 利用iframe的history和主应用的history在同一个top-level browsing context来搭建天然的路由同步机制
- 副作用局限在沙箱内部,子应用切换无需任何清理工作,没有额外的切换成本
支持vite esmoudle加载,由于js是独立在iframe中加载的,所以支持esmodule加载
支持预加载
支持应用保活,子应用状态保留,由于是独立在iframe中的,而切换应用时不会移除iframe,所以子应用的状态会被保留在原来的iframe中,当主应用再次渲染子应用dom时,会显示之前的状态。
多应用同时激活在线
# 缺点
iframe沙箱的src设置了主应用的host,初始化iframe的时候需要等待iframe的location.orign从’about:blank’初始化为主应用的host,这个采用的计时器去等待的不是很优雅
# Micro App
Micro App (opens new window) 是京东出的一款基于 Web Component 原生组件进行渲染的微前端框架,不同于目前流行的开源框架,它从组件化的思维实现微前端,旨在降低上手难度、提升工作效率。
采用iframe沙箱模式,这点和wujie的方案一样了,都是webComponent + iframe
# 实现方案
首先micro-app实现了一个基于WebComponent的组件,并实现了类Shadow Dom 的效果,开发者只需要用来加载子应用,整个对子应用的加载、js隔离、css隔离的逻辑都封装在了web component组件中,具体来说:
- 当调用microApp.start()后,会注册一个名为micro-app 的自定义 webComponent 标签。我们可以从 中拿到子应用的线上入口地址。
- 组件内部,当匹配到路由后,跟qiankun一样加载html,得到html字符串模版
- 分析html字符串,提取头和,并替换为框架自定义标签和
- 在
<micro-app-head>
内,会对script标签和link标签的内容进行加载并执行 - 将
<micro-app-head>
和<micro-app-body>
插入到<micro-app>
标签内 <micro-app>
内提供了js沙箱方法(v1.0以前跟qiankun沙箱一样),<micro-app-head>
挂载到<micro-app>
后,内部会逐一对<micro-app-head>
内的script
标签的 js 绑定作用域,实现 js 隔离
# 优点
接入简单,组件式引入子应用
团队持续更新维护
js隔离、css隔离、路由同步
支持子应用保活, 需要开启keep-alive模式 支持fiber模式,提升主应用的渲染性能。
# 缺点
1.0之前不支持vite,1.0之后支持了
默认css隔离方式,主应用的样式还是会污染到子应用。
子应用和主应用必须相同的路由模式,要么同时hash模式,要么同时history模式
依赖于CustomElements和Proxy两个较新的API。Proxy暂时没有做兼容,所以对于不支持Proxy的浏览器无法运行micro-app。
个人是比较看好 WebComponent 化的,侵入较低,接入更流畅。
它是由google推出的浏览器的原生组件,具体内容查看 链接 (opens new window) 了解
- 复用性:不需要对外抛出加载和卸载的全局 API,可复用能力更强
- 标准化:W3C 的标准,未来能力会得到持续升级
- 插拔性:可以非常便捷的进行移植和组件替换
笔记
切记不要以为微前端而去微前端,老旧项目带来的迁移成本,不同项目技术栈的兼容与边界问题处理,因为没有迫切的需求而接入微前端,只会带来额外的负担,很多时候,iframe 其实就很够用了。