微前端概述
Techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently. – Micro Frontends
什么是微前端
微前端是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。各个前端应用还可以独立运行、独立开发、独立部署。微前端架构与框架无关,每个微应用都可以使用不同的框架。
微前端的价值
1. 项目增量迁移
迁移是一项非常耗时且艰难的任务,比如有一个管理系统使用 AngularJS 开发维护已经有几年时间,但是随时间的推移和团队成员的变更,无论从开发成本还是用人需求上,AngularJS 已经不能满足要求,于是团队想要更新技术栈,想在其他框架中实现新的需求,但是现有项目怎么办?直接迁移是不可能的,在新的框架中完全重写也不太现实。
使用微前端架构就可以解决问题,在保留原有项目的同时,可以完全使用新的框架开发新的需求,然后再使用微前端架构将旧的项目和新的项目进行整合。这样既可以使产品得到更好的用户体验,也可以使团队成员在技术上得到进步,产品开发成本也降到的最低。
2. 技术选型灵活
使用微前端架构,主框架不限制接入应用的技术栈,微应用具备完全自主权
3. 更快且独立的发布
在目前的单页应用架构中,使用组件构建用户界面,应用中的每个组件或功能开发完成或者bug修复完成后,每次都需要对整个产品重新进行构建和发布,任务耗时操作上也比较繁琐。
在使用了微前端架构后,可以将不能的功能模块拆分成独立的应用,此时功能模块就可以单独构建单独发布了,构建时间也会变得非常快,应用发布后不需要更改其他内容应用就会自动更新,这意味着你可以进行频繁的构建发布操作了。
3. 允许单个团队做出技术决策
因为微前端构架与框架无关,当一个应用由多个团队进行开发时,每个团队都可以使用自己擅长的技术栈进行开发,也就是它允许适当的让团队决策使用哪种技术,从而使团队协作变得不再僵硬。
微前端使用场景
- 拆分巨型应用,使应用变得更加可维护
- 兼容历史应用,实现增量开发
微前端如何实现
1. 多个微应用如何进行组合 ?
在微前端架构中,除了存在多个微应用以外,还存在一个容器应用,每个微应用都需要被注册到容器应用中。
微前端中的每个应用在浏览器中都是一个独立的 JavaScript 模块,通过模块化的方式被容器应用启动和运行。
使用模块化的方式运行应用可以防止不同的微应用在同时运行时发生冲突。
2. 在微应用中如何实现路由 ?
在微前端架构中,当路由发生变化时,容器应用首先会拦截路由的变化,根据路由匹配微前端应用,当匹配到微应用以后,再启动微应用路由,匹配具体的页面组件。
3. 微应用与微应用之间如何实现状态共享 ?
在微应用中可以通过发布订阅模式实现状态共享,比如使用 RxJS。
4. 微应用与微应用之间如何实现框架和库的共享?
可以通过 import-map
和 webpack 中的 externals
属性。
在微应用架构中我们要使用模块化的方式去加载应用。
在最新的模块化当中新增了 import-map
特性,允许我们加载网络模块,而不是一定要将模块下载到本地,我们只需要配置好模块名字和对应的模块地址即可,这样每个微应用在引用公共模块时都可以引用提前配置好的公共模块了。
我们还需要修改微应用本身的 webpack 配置,通过 webpack 的 externals
属性告诉 webpack 在打包应用时哪些模块是不需要被打包的。
常见的微前端解决方案对比
使用 HTTP 服务器(Nginx)的路由来重定向多个微应用
可以理解为把一个大型项目拆分成多个微模板应用,每个微模板应用就代表一个前端服务,然后通过 Nginx 配置代理映射到不同的子模板应用上,这也叫路由分发式微前端
,是一个非常古老且传统的方法;
这种方式看上去更像是多个前端应用的聚合,即我们只是将这些不同的前端应用拼凑到一起,使他们看起来像是一个完整的整体。但是它们并不是,每次用户从 A 应用到 B 应用的时候,往往需要刷新一下页面。
优点: 简单、快速、易配置
缺点: 在切换应用时会触发浏览器刷新,影响体验
使用 iframe 及自定义消息传递机制
就其性质而言,iframe 可以轻松地从独立的子页面构建页面。它们还在样式和全局变量方面提供了很好的隔离度,不会相互干扰。
优点:
- 实现简单: 子应用之间自带沙箱,天然隔离,互不影响
- 技术不限制: 可以各自使用完全不同的前端框架;
- 消息传递: 只要每个 iframe 来自同一个来源,可以使用 Window.postMessageAPI 来进行消息传递;
缺点:
- Bundle 的大小各异: 构建时也不能提取公共依赖关系;
- 无 SEO: 众所周知 iframe 对 SEO 是毁灭性的打击,单这一条即可否定该方案了;
- URL 状态不同步: iframe 的页面 url 中的状态信息并不能同步到父窗口,无法使用浏览器的前进后退功能;
- DOM 结构不共享: iframe 的页面布局只针对于 iframe 窗口(例如:全局弹框无法给出合理布局);
- 全局上下文完全隔离,内存变量不共享: iframe 内外系统的通信、数据同步等需求,主应用的 cookie 要透传到根域名都不同的微应用中实现免登效果;
- 慢: 每次微应用进入都是一次浏览器上下文重建、资源重新加载的过程;
使用 Web Components 构建应用
Web Components
是一个 Web 标准,像 Angular、React/Preact、Vue 或 Hyperapp 这样的主流 JavaScript 框架都支持它们;
你可以将 Web Components
视为使用开放 Web 技术创建的可重用的用户界面小部件。
优点:
- 拥有自己独立的
Scripts
和Styles
,以及对应的用于单独部署子应用组件的域名; - 代码的可读性变得非常清晰,组件资源内部高内聚,组件资源由自身加载控制;
缺点:
- 浏览器和框架的支持不够: 需要更多的 polyfills 从而影响到用户页面的加载体验;
- 重写现有的前端应用: 我们需要在整个前端应用上把它们全部转换成 Web Components;
- 系统架构复杂: 当应用被拆分为一个又一个的组件时,组件间的通讯就成了一个特别大的麻烦
Module Federation 模块联邦
webpack5
新增的 Module Federation(模块联邦)
功能,他可以帮助将多个独立的构建组成一个应用程序,不同的构建可以独立的开发与部署,利用模块联邦我们可以在一定程度上去实现微前端。
优点:
- 开箱即用:只需要执行几行命令即可拉取相应的模板代码并把项目跑起来,包括基座应用和微前端应用,无需处理构建工具的复杂配置;
- 独立开发与部署:基于提供的代理工具,微应用开发者在单独开发微应用时,无需启动基座或者其它微应用;
- 去中心化: 因为采用的是模块共享,所以不用使用中心基座的概念;
- 组件共享: 与 npm 发包类似的组件共享管理;
缺点:
- 无法沙箱隔离: 需借助其它工具和框架才能做到应用层面的隔离;
- 技术单一: 仅限使用 webpack5 版本以上;
- 代码封闭性高: 依旧需要做npm那一套管理和额外的拉取代码,还不如直接 npm 复用方便;
- 拆分粒度需要权衡: 共享的 lib 无法做到 tree-shaking;
- 依赖前置: 导致时间加载变长;
组合式应用路由分发(中心基座方案)
当下微前端主要采用的是 组合式应用路由方案
,该方案的核心是 主从
思想,即包括一个基座(MainApp)应用 和 若干个微(MicroApp)应用;
基座应用大多数是一个前端SPA项目,主要负责 应用注册,路由映射,消息下发
等,而微应用是独立前端项目,这些项目不限于采用 React,Vue,Angular 或者 JQuery 开发,每个微应用注册到基座应用中,由基座进行管理,但是如果脱离基座也是可以单独访问。
优点:
- 技术不限制: 可以各自使用完全不同的前端框架;
- 无感切换: 因为是一个 SPA 项目,所以体验极佳;
- 利于SEO
- 独立开发与部署
- 微前端优势几乎都有
缺点:
- 沙箱不隔离: 也就是 js 与 css 样式会出现冲突的问题
微前端框架选型
qiankun
qiankun 是基于 single-spa
的微前端方案
特点
- html entry 的方式引入子应用,相比 js entry 极大的降低了应用改造的成本
- 完备的沙箱方案
- js 沙箱做了 SnapshotSandbox、LegacySandbox、ProxySandbox 三套渐进增强方案
- css 沙箱做了 strictStyleIsolation、experimentalStyleIsolation 两套适用不同场景的方案
- 做了静态资源预加载能力
- !适配成本比较高,工程化、生命周期、静态资源路径、路由等都要做一系列的适配工作
- !css 沙箱采用严格隔离会有各种问题,js 沙箱在某些场景下执行性能下降严重
- !无法同时激活多个子应用
- !无法支持 vite 等 esmodule 脚本运行
成本
接入成本
- 子应用需要接入生命周期代码;
- 主应用需要接入注册微应用代码;
microApp
micro-app 是基于 webcomponent 的微前端方案
特点
- 使用 webcomponet 加载子应用相比 single-spa 这种注册监听方案更加优雅
- 基于CustomElement和样式隔离、js隔离来实现微应用的加载,所以子应用无需改动就可以接入
- 支持应用隔离
- 通过劫持底层接口实现了元素隔离
- 降低子应用改造的成本,提供静态资源预加载能力
- 组件式的 api 更加符合使用习惯
- !支持 vite 运行,但必须使用 plugin 改造子应用,且 js 代码没办法做沙箱隔离
成本
接入成本
子应用无需改动,主应用需要接入微应用代码
EMP
EMP 是基于 webpack 5 module federation 的微前端方案
特点
- webpack 联邦编译可以保证所有子应用依赖解耦
- 应用间去中心化的调用、共享模块
- 模块远程 ts 支持
- !对 webpack 强依赖,老旧项目不友好
- !没有有效的 css 沙箱和 js 沙箱,需要靠用户自觉
- !主、子应用的路由可能发生冲突
wujie
无界 是基于 webcomponent 容器 + iframe 沙箱 的微前端方案
特点
- webcomponet 部分的有点同microApp
- 适配vite更加简便
总结
方案选择
优先考虑 qiankun、microApp、wujie,加载微应用的方式都是通过http的方式,可以实现主应用和微应用之间的解耦
接入成本
优先考虑 microApp、wujie,接入成本更低
兼容性
优先考虑 microApp、wujie,因为qiankun改造成本相对来说更大,并且不支持vite
等 ESM
脚本运行
性能
优先考虑 microApp,microApp推出最新版本MicroApp V1.0.0-rc
后,性能已接近原生