微前端那些事儿

什么是微前端?微前端可以理解成借鉴了微服务架构思想,聚合多个子应用,有减少应用间的耦合,提升应用的扩展性。

微前端核心

相比一整块的前端仓库,微前端架构下的前端仓库倾向于更小更灵活。有一个基座应用(主应用),来管理各个子应用的加载和卸载。所以微前端不是指具体的库,不是指具体的框架,不是指具体的工具,而是一种理想与架构模式。

微前端的核心三大原则:独立开发、独立部署、独立运行。

image-20240910015230334

微前端应用场景

  • 增量升级:对于已有的旧项目,一时半会又不能完全重构,很难做全量的技术升级,而微前端是一个很好的实施渐进式重构的策略。
  • 想独立部署每一个单页应用。
  • 项目团队比较分散,跨部门等,我们需要把用一个主项目聚合分散的子项目。
  • 基于多页的子应用缺乏管理,规范/标准不统一,无法统一控制视觉呈现、共享的功能和依赖,造成重复工作。

微前端需要解决的问题

以后台管理系统为例

最外层是基座,基座是微前端应用集成的一个重要平台。同时也肩负着管理公共资源、依赖、规范的责任。主要有以下职责:

(1)子应用集成,给子应用提供渲染容器

(2)权限管理

(3)会话管理

(4)路由、菜单管理

(5)主题管理

(6)共享依赖

(7)多语言管理(最重要的一点)等

content 里面可以任意放不同技术的子应用,我们只需要开发一个主应用(主应用也可以自由选择语言,目前支持 react、vue、vite、angular、next.js、nuxt.js),将一些分散的应用接进来,主应用还可以通过控制权限,让不同的账号看到的菜单不一样,即看到不同系统的页面,通过同一个地址访问到不同的子应用。

image-20240910033729633

归纳

  • 父子、兄弟应用之间的数据通信
  • 组件、公共依赖复用
  • 子应用免登录
  • 跨域问题: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 的一些优缺点:

优点:

  1. 框架无关性single-spa 几乎支持任何前端框架,无论是 React、Vue、Angular 还是其他框架,都可以集成到 single-spa 中 。
  2. 生命周期管理single-spa 从现代框架的组件生命周期中获得灵感,将生命周期应用于整个应用程序,使得应用的加载、挂载和卸载更加可控 。
  3. 路由管理single-spa 允许定义路由规则,以确定在给定的 URL 下哪个应用程序是活动的,从而实现微前端应用的动态加载和卸载 。
  4. 样式隔离single-spa 提供了 single-spa-css 工具来帮助管理子应用的样式加载和卸载,以及使用 Scoped CSS 来实现样式隔离 。
  5. 全局变量隔离:通过 single-spa-leaked-globals 插件,single-spa 能够隔离全局变量,防止不同应用间的变量冲突 。

缺点:

  1. 文档和学习曲线single-spa 的文档可能略显凌乱,对于初学者来说可能难以抓住重点,需要一定的学习曲线 。
  2. 隔离不彻底:虽然 single-spa 提供了全局变量隔离,但它采用的是快照模式,可能无法保证多个子应用同时运行时的有效隔离 。
  3. 需要额外的工具:为了实现样式隔离和全局变量隔离,single-spa 需要依赖额外的工具和插件,这可能会增加项目的复杂性 。
  4. 可能的性能问题:如果不正确地管理子应用的加载和卸载,可能会导致不必要的资源加载,从而影响性能 。

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 两套适用不同场景的方案(类vuescoped
  • 提供多应用并行与多实例沙箱支持,同一时刻,渲染多个微应用,使一个应用可以加载多个其他应用的模块
  • 做了静态资源预加载能力
  • 应用间通信简单,全局注入

不足

  • 适配成本比较高,工程化、生命周期、静态资源路径、路由等都要做一系列的适配工作
  • 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] 微前端方案

[4] 一文通透讲解 webpack5 module federation