微前端那些事儿
什么是微前端?微前端可以理解成借鉴了微服务架构思想,聚合多个子应用,有减少应用间的耦合,提升应用的扩展性。
微前端核心
相比一整块的前端仓库,微前端架构下的前端仓库倾向于更小更灵活。有一个基座应用(主应用),来管理各个子应用的加载和卸载。所以微前端不是指具体的库,不是指具体的框架,不是指具体的工具,而是一种理想与架构模式。
微前端的核心三大原则:独立开发、独立部署、独立运行。
微前端应用场景
- 增量升级:对于已有的旧项目,一时半会又不能完全重构,很难做全量的技术升级,而微前端是一个很好的实施渐进式重构的策略。
- 想独立部署每一个单页应用。
- 项目团队比较分散,跨部门等,我们需要把用一个主项目聚合分散的子项目。
- 基于多页的子应用缺乏管理,规范/标准不统一,无法统一控制视觉呈现、共享的功能和依赖,造成重复工作。
微前端需要解决的问题
以后台管理系统为例
最外层是基座,基座是微前端应用集成的一个重要平台。同时也肩负着管理公共资源、依赖、规范的责任。主要有以下职责:
(1)子应用集成,给子应用提供渲染容器
(2)权限管理
(3)会话管理
(4)路由、菜单管理
(5)主题管理
(6)共享依赖
(7)多语言管理(最重要的一点)等
content 里面可以任意放不同技术的子应用,我们只需要开发一个主应用(主应用也可以自由选择语言,目前支持 react、vue、vite、angular、next.js、nuxt.js),将一些分散的应用接进来,主应用还可以通过控制权限,让不同的账号看到的菜单不一样,即看到不同系统的页面,通过同一个地址访问到不同的子应用。
归纳
- 父子、兄弟应用之间的数据通信
- 组件、公共依赖复用
- 子应用免登录
- 跨域问题:CORS、代理服务器
- JS、CSS 沙箱环境
- DOM 交互
- 状态管理
- 子应用隔离
- 路由同步
- 性能优化:预加载、懒加载、代码分割
微前端方案
iframe
iframe 是实现微前端的一种技术手段,它具有一些明显的优点和缺点。
优点:
简单性:使用 iframe 作为微前端的实现方式相对简单,可以轻松嵌入不同的子应用。
隔离性: iframe 提供了天然的隔离环境,使得各个子应用之间的 JavaScript、CSS 和 DOM 完全隔离,避免了样式和脚本冲突。
多应用激活:可以在一个页面上同时激活多个 iframe ,从而组合不同的业务应用。
安全性:由于隔离性, iframe 可以提高应用的安全性,防止潜在的跨站脚本攻击(XSS)。
缺点:
- 路由不同步: iframe 中的路由状态在刷新或导航后可能会丢失,导致用户无法正常使用后退和前进按钮。
- DOM 割裂: iframe 内的 DOM 无法与主页面的 DOM 交互,这限制了某些需要全局交互的功能,如模态弹窗和遮罩层。
- 通信困难: iframe 之间的通信需要通过 postMessage API 进行,这比在同一应用内部进行通信要复杂。
- 性能问题:每个 iframe 都是一个完整的页面上下文,每次加载都需要重新加载资源,可能导致性能下降和加载时间延长。
- SEO 不友好:搜索引擎优化方面, iframe 内容通常不会被搜索引擎索引,这可能影响网站的搜索引擎排名。
为了解决 iframe 的缺点,一些现代的微前端框架和解决方案,如无界微前端框架,尝试通过技术手段来弥补这些不足。例如,通过使用 Shadow DOM 和 Web Components 来实现样式和 DOM 的隔离,同时提供更好的通信机制和路由同步策略。这些方法旨在保留 iframe 的优点,同时减少其缺点带来的影响。
使用 iframe 作为微前端架构的实现方式时,可以通过以下策略来优化和弥补其缺点:
- 路由同步:
URL 参数:通过 URL 参数或查询字符串来同步 iframe 内的路由状态。
自定义事件:在主应用和 iframe 之间使用自定义事件或 postMessage 来同步路由变化。
DOM 交互:
代理模式:在 iframe 内部拦截 DOM 操作,并通过代理将操作转发到主应用的 DOM 中。
Shadow DOM:使用 Shadow DOM 来封装 iframe 内的 DOM,并通过主应用中的容器来实现全局 UI 组件。
通信优化:
事件总线:建立一个事件总线,让主应用和 iframe 内的子应用能够订阅和发布事件。
共享状态管理:使用共享状态管理库(如 Redux、Vuex 或 NgRx)来同步状态。性能优化:
预加载:预加载 iframe 资源,减少用户等待时间。
懒加载:根据用户滚动或视窗位置来懒加载 iframe 。
代码分割:使用代码分割和按需加载技术,减少 iframe 的初始加载体积。SEO 优化:
服务器端渲染:对于关键内容,使用服务器端渲染(SSR)来提供给搜索引擎爬虫。
动态 iframe :根据爬虫的 User-Agent 动态加载 iframe 内容。跨域问题:
CORS:确保子应用支持跨域资源共享(CORS),以便主应用可以访问子应用的资源。
代理服务器:使用代理服务器来绕过跨域限制。用户体验:
隐藏加载:在 iframe 加载期间显示加载动画或占位符,提高用户体验。
错误处理:为 iframe 加载失败提供错误处理逻辑和用户提示。样式隔离:
CSS 模块:使用 CSS 模块或 Scoped CSS 来避免样式冲突。
命名空间:为 iframe 内的元素添加特定的类名前缀,以防止样式冲突。
状态管理:
全局状态:通过全局状态管理来同步主应用和 iframe 内的状态。框架支持:
无界框架:使用如无界(Wujie)这样的框架,它通过结合 iframe 和 Web Components 来解决 iframe 的缺点。
通过这些策略,可以在很大程度上减轻 iframe 作为微前端实现时的缺点,提高整体架构的可用性和性能。
single-spa
single-spa
是一个流行的微前端框架,它允许将多个单页面应用(SPA)聚合成一个整体应用。以下是 single-spa
的一些优缺点:
优点:
- 框架无关性:
single-spa
几乎支持任何前端框架,无论是 React、Vue、Angular 还是其他框架,都可以集成到single-spa
中 。 - 生命周期管理:
single-spa
从现代框架的组件生命周期中获得灵感,将生命周期应用于整个应用程序,使得应用的加载、挂载和卸载更加可控 。 - 路由管理:
single-spa
允许定义路由规则,以确定在给定的 URL 下哪个应用程序是活动的,从而实现微前端应用的动态加载和卸载 。 - 样式隔离:
single-spa
提供了single-spa-css
工具来帮助管理子应用的样式加载和卸载,以及使用 Scoped CSS 来实现样式隔离 。 - 全局变量隔离:通过
single-spa-leaked-globals
插件,single-spa
能够隔离全局变量,防止不同应用间的变量冲突 。
缺点:
- 文档和学习曲线:
single-spa
的文档可能略显凌乱,对于初学者来说可能难以抓住重点,需要一定的学习曲线 。 - 隔离不彻底:虽然
single-spa
提供了全局变量隔离,但它采用的是快照模式,可能无法保证多个子应用同时运行时的有效隔离 。 - 需要额外的工具:为了实现样式隔离和全局变量隔离,
single-spa
需要依赖额外的工具和插件,这可能会增加项目的复杂性 。 - 可能的性能问题:如果不正确地管理子应用的加载和卸载,可能会导致不必要的资源加载,从而影响性能 。
single-spa
提供了一套灵活的机制来构建和管理微前端架构,但同时也需要开发者对框架有深入的理解,以便充分利用其提供的功能并避免潜在的问题。
了解更多,详见
qiankun
qiankun ,阿里出品,是基于 single-spa 的微前端方案。
与路由绑定的方式渲染微应用
以路由(url)为维度来划分微应用,基于 qiankun 的 registerMicroApps
API,提供 entry html 地址,并为其分配一个路由规则即可。路由与应用绑定的方式简单直观,是微前端中最为常见的使用场景,通常我们会用这种方式将一堆独立域名访问的 MPA 应用,整合成一个一体化的 SPA 应用。
局限性:
- 由于 URL/路由 的 唯一性/排他性 的特点,这种方式只适用单实例场景需求
- 微应用的调度都是由路由系统来自动处理的,虽然省事但是碰到更复杂的需求,如同一个路由下,根据不同的用户权限展示不同的微应用这类个性化诉求,需要写一些中间层代码来曲线救国
- 应用挂载的容器节点等需提前准备好,不然碰到 动态/嵌套 路由这类情况,可能会因为路由 listener 监听触发的时序不确定,导致微应用无法完成挂载的问题
以组件的方式使用微应用
qiankun 2.0 的发布带来一个全新的 API loadMicroApp
,通过这个 API 我们可以自己去控制一个微应用加载/卸载。开发者可以在脱离路由的限制下,以更自由的方式去渲染我们的微应用。基于 loadMicroApp
API,我们只需要做一些简单的封装,即可以类似组件的开发体验,完成微应用的接入。这类方式适用于一些可共用的、带业务逻辑的服务型组件。通过组件的这种方式,我们可以完全自主的控制微应用的渲染,并与之做一些复杂的交互。不论是在开发者的编码心智,还是用户的体验上,都跟使用一个普通的业务组件无异。
局限性:
组件的方式非常灵活,几乎解决了所有路由绑定方式渲染微应用的问题,但也有自己的一些局限:需要确保被加载的微应用是不含自己的路由系统,否则会出现多个应用间路由系统互相 抢占/冲突
的情况。
特点
- html entry 的方式引入子应用,相比 js entry 极大的降低了应用改造的成本
- 监听路由自动的加载、卸载当前路由对应的子应用
- 路由保持,浏览器刷新、前进、后退,都可以作用到子应用
- 完备的沙箱方案,js 沙箱做了 SnapshotSandbox、LegacySandbox、ProxySandbox 三套渐进增强方案,css 沙箱做了 strictStyleIsolation、experimentalStyleIsolation 两套适用不同场景的方案(类
vue
的scoped
) - 提供多应用并行与多实例沙箱支持,同一时刻,渲染多个微应用,使一个应用可以加载多个其他应用的模块
- 做了静态资源预加载能力
- 应用间通信简单,全局注入
不足
- 适配成本比较高,工程化、生命周期、静态资源路径、路由等都要做一系列的适配工作
- css 沙箱无法绝对的隔离,采用严格隔离会有各种问题;js 沙箱在某些场景下执行性能下降严重
- 基于路由匹配,无法同时激活多个子应用,也不支持子应用保活
- 有一定的改造成本,从
webpack
、代码、路由等等都要做一系列的适配 - 无法支持 vite 等 ESM 脚本运行
了解更多,详见
MicroApp
micro-app ,京东出品,是基于 webcomponent + qiankun sandbox 的微前端方案。
特点
- 使用 webcomponet 加载子应用相比 single-spa 这种注册监听方案更加优雅
- 复用经过大量项目验证过 qiankun 的沙箱机制也使得框架更加可靠
- 组件式的 api 更加符合使用习惯,支持子应用保活
- 降低子应用改造的成本,提供静态资源预加载能力
不足
- 接入成本较 qiankun 有所降低,但是路由依然存在依赖
- 多应用激活后无法保持各子应用的路由状态,刷新后全部丢失
- css 沙箱依然无法绝对的隔离,js 沙箱做全局变量查找缓存,性能有所优化
- 支持 vite 运行,但必须使用 plugin 改造子应用,且 js 代码没办法做沙箱隔离
- 对于不支持 webcompnent 的浏览器没有做降级处理
EMP
EMP 方案是基于 webpack 5 module federation 的微前端方案。
特点
- webpack 联邦编译可以保证所有子应用依赖解耦
- 应用间去中心化的调用、共享模块
- 模块远程 ts 支持
不足
- 对 webpack 强依赖,老旧项目不友好
- 没有有效的 css 沙箱和 js 沙箱,需要靠用户自觉
- 子应用保活、多应用激活无法实现
- 主、子应用的路由可能发生冲突
了解更多,详见
wujie
wujie 无界,腾讯出品,微前端的功能非常强大,支持子应用保活、子应用内嵌、多应用激活、去中心化通信、生命周期、插件系统、vite 框架支持、兼容 IE9、应用共享。
了解更多,详见
相关链接
[1] 浅入深出的微前端 MicroApp | 京东云技术团队
[2] 微前端技术方案调研(qiankun、micro-app、wujie)
[3] 微前端方案