微前端总结
# 什么是微前端
提示
Techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently. -- Micro Frontends
微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。
微前端架构具备以下几个核心价值:
技术栈无关
主框架不限制接入应用的技术栈,微应用具备完全自主权
独立开发、独立部署
微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新
增量升级
在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略
独立运行时
每个微应用之间状态隔离,运行时状态不共享
微前端架构旨在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增多、变迁,从一个普通应用演变成一个巨石应用(Frontend Monolith)后,随之而来的应用不可维护的问题。这类问题在企业级 Web 应用中尤其常见。
# 微前端调研
主流框架:
qiankun(乾坤) qiankun 是蚂蚁金融科技基于微前端架构的云产品统一接入平台,基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。
micro-app micro-app是由京东前端团队推出的一款微前端框架,它借鉴了WebComponent的思想,通过js沙箱、样式隔离、元素隔离、路由隔离模拟实现了ShadowDom的隔离特性,并结合CustomElement将微前端封装成一个类WebComponent组件,从而实现微前端的组件化渲染,旨在降低上手难度、提升工作效率。和技术栈无关,也不和业务绑定,可以用于任何前端框架。
wujie(无界) 无界微前端解决方案源于腾讯的低代码平台,是一款基于 Web Components + iframe 微前端框架,具备成本低、速度快、原生隔离、功能强等一系列优点。
# iframe方案
特点
接入比较简单
隔离非常稳完美
不足
dom割裂感严重,弹框只能在iframe,而且有滚动条
通讯非常麻烦,而且刷新iframe url状态丢失
前进后退按钮无效
2
3
4
5
6
7
8
# qiankun方案
特点
html entry 的方式引入子应用,相比 js entry 极大的降低了应用改造的成本;
完备的沙箱方案,js 沙箱做了 SnapshotSandbox、LegacySandbox、ProxySandbox 三套渐进增强方案,css 沙箱做了 strictStyleIsolation、experimentalStyleIsolation 两套适用不同场景的方案;
做了静态资源预加载能力;
不足
适配成本比较高,工程化、生命周期、静态资源路径、路由等都要做一系列的适配工作;
css 沙箱采用严格隔离会有各种问题,js 沙箱在某些场景下执行性能下降严重;
无法同时激活多个子应用,也不支持子应用保活;
无法支持 vite 等 esmodule 脚本运行;
2
3
4
5
6
7
8
9
10
底层原理 js沙箱使用的是proxy进行快照然后用用 with(window){} 包裹起来 with内的window其实就是proxy.window 我们声明变量 var name = '小满' 实际这个变量挂到了proxy.window 并不是真正的window css沙箱原理 第一个就是shadowDom隔离 第二个类似于Vue的scoped。
qiankun中js 沙箱做了 SnapshotSandbox、LegacySandbox、ProxySandbox 三套渐进增强方案的处理方式,
- 把主应用的 window 对象做浅拷贝,将 window 的键值对存成一个 Hash Map。之后无论微应用对 window 做任何改动,当要在恢复环境时,把这个 Hash Map 又应用到 window 上就可以了。
- LegacySandbox 沙箱与 SnapshotSanbox沙箱实现思路差不多,都是通过记录新增或改变的属性,从而实现对环境的还原和隔离。但是LegacySandbox与SnapshotSanbox的对比方式略有不同,LegacySandbox使用es6的Proxy创建一个window的proxy对象,所有调用window.xxx的操作,都代理到proxy上,并且在这上面进行一系列记录属性变更的操作,这也是LegacySandbox比SnapshotSanbox性能高的原因。不支持多实例,在同一个页面,同时激活多个子应用可能会有异常。因为无法监听对window的更改,是哪一个子应用进行的,所以异常会难以排查。
- ProxySanbox沙箱,会把当前 window 的一些原生属性(如document, location等)拷贝出来,单独放在一个对象上,这个对象也称为fakeWindow,它会为每个子应用分配一个fakeWidnow。支持多实例,在同一个页面,可以同时激活多个子应用。每个子应用使用独立的fakewindow。
- 将主应用中的window原生属性进行浅拷贝,放在一个对象(fakeWindow)上
- 为每一个子应用分配一个fakeWindow,作为子应用的window
- 子应用访问和改写window对象属性时,都会被拦截
- 当子应用中新增\改写window上的属性时:如果改变的是原生属性(location、document等),则修改window,如果不是,则修改fakewindow
- 当子应用读取window上的属性时:如果读取的时原生属性(location,document等),则读取window,如果不是,则访问fakewindow上的属性
# micro-app 方案
特点
使用 webcomponet 加载子应用相比 single-spa 这种注册监听方案更加优雅;
复用经过大量项目验证过 qiankun 的沙箱机制也使得框架更加可靠;
组件式的 api 更加符合使用习惯,支持子应用保活;
降低子应用改造的成本,提供静态资源预加载能力;
不足
接入成本较 qiankun 有所降低,但是路由依然存在依赖; (虚拟路由已解决)
多应用激活后无法保持各子应用的路由状态,刷新后全部丢失; (虚拟路由已解决)
css 沙箱依然无法绝对的隔离,js 沙箱做全局变量查找缓存,性能有所优化;
支持 vite 运行,但必须使用 plugin 改造子应用,且 js 代码没办法做沙箱隔离;
对于不支持 webcompnent 的浏览器没有做降级处理;
2
3
4
5
6
7
8
9
10
11
12
底层原理 js隔离跟qiankun类似也是使用proxy + with,css隔离自定义前缀类似于scoped
# EMP 方案
特点
webpack 联邦编译可以保证所有子应用依赖解耦;
应用间去中心化的调用、共享模块;
模块远程 ts 支持;
不足
对 webpack 强依赖,老旧项目不友好;
没有有效的 css 沙箱和 js 沙箱,需要靠用户自觉;
子应用保活、多应用激活无法实现;
主、子应用的路由可能发生冲突;
2
3
4
5
6
7
8
9
10
底层原理 这个东西有点类似于拆包,也可以叫模块共享,例如React有个模块可以共享给Vue项目用Vue2的组件可以共享给Vue3用。
# 无界 方案
特点
接入简单只需要四五行代码
不需要针对vite额外处理
预加载
应用保活机制
不足
隔离js使用一个空的iframe进行隔离
子应用axios需要自行适配
iframe沙箱的src设置了主应用的host,初始化iframe的时候需要等待iframe的location.orign从'about:blank'初始化为主应用的host,这个采用的计时器去等待的不是很优雅。
2
3
4
5
6
7
8
9
10
底层原理 使用shadowDom 隔离css,js使用空的iframe隔离,通讯使用的是proxy
# 虚拟路由
虚拟路由系统与浏览器的路由行为一致,它通过自定义 location 和 history 等核心路由 API,重写了 popState 和 hashChange 事件,拦截路由导航和事件,并提供了一系列自定义 API,模拟了在浏览器环境下的 Web 应用程序的渲染、跳转和返回等路由行为。子应用程序在这个虚拟路由系统中运行,与基座应用程序的路由相互隔离,从而避免相互影响,并增强了子应用程序与基座应用程序之间的交互能力。通过虚拟路由系统,基座应用程序可以方便地获取子应用程序的路由信息并控制子应用程序的跳转,子应用程序的路由信息会作为参数同步到浏览器地址上。此外,虚拟路由系统还提供了许多功能,帮助开发人员提高工作效率。
# web components
webcomponents相关文章 (opens new window)
- 组件库开发 可以用于构建大型的组件库,如UI组件库。像按钮、表单、菜单等组件都可以作为Web Component进行开发。这样的组件库可以被不同的项目复用,提高开发效率。例如,一个公司可以开发一套内部使用的UI组件库,包含各种风格统一的组件,供各个产品团队使用。
- 微前端架构 在微前端架构中,每个微应用可以被封装成一个Web Component。不同的团队可以独立开发自己的微应用,然后将它们组合成一个完整的应用。例如,一个电商网站可以有产品展示微应用、购物车微应用、用户登录微应用等,这些微应用可以通过Web Component的方式集成在一起。
# 如何样式隔离
- BEM(Block Element Modifier)约定项目前缀
- css-Modules 打包时生成不冲突的选择器名
- Shadow Dom 真正意义上的隔离
- css-in-js
<div id="shadow"></div>
<script>
let shadowDOM = shadow.attachShadow({mode:"close"}); // 外界无法访问shadow dom 其中shadow相当于document.getElementById('shadow').attachShadow
let pElm = document.createElement("p");
pElm.innerHTML = "hello world";
let styleElm = document.createElement("style");
styleElm.textContent = `
p{
color:red;
}
`
shadowDom.appendChild(styleElm);
shadowDOM.appendChild(pElm);
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 如何通讯
- 基于url进行数据传递,但传递消息能力弱
- 基于customEvent实现通讯
- 基于props进行主子组件通信
- 使用全局变量、Redux进行通讯
更多关于微前端的相关介绍,推荐大家可以去看这几篇文章:
- Micro Frontends (opens new window)
- Micro Frontends from martinfowler.com (opens new window)
- 可能是你见过最完善的微前端解决方案 (opens new window)
- 微前端的核心价值 (opens new window)
相关配置介绍可以查看 webpack 相关文档 (opens new window)。