在实施微前端架构时,前端框架和技术的选型是非常重要的。不同的框架和技术有着不同的优缺点,需要结合具体的应用场景进行选择。
一、常见的微前端解决方案
Web Components
Web Components(包括Custom Elements、Shadow DOM和HTML Imports)是浏览器原生支持的组件封装技术,允许开发者创建可复用的自定义元素,这些元素包含了完整的HTML结构、CSS样式和JavaScript逻辑。这种封装机制为微前端架构的实现提供了天然的土壤,使得不同子应用可以在同一页面上共存,同时保持各自的独立性和封装性。
-
原理:利用标准化的Web组件技术封装各个子应用,每个子应用作为一个独立的可复用的Web Component或自定义元素,这些组件可以直接在DOM中插入并独立运行。
优点:
-
CSS和JavaScript天然隔离,避免了样式冲突和脚本污染,拥有对应的用于单独部署子应用组件的域名
-
多个子应用可以并存,支持并行开发和独立部署
-
代码的可读性变得非常清晰,组件资源内部高内聚,组件资源由自身加载控制
-
原生浏览器支持,不依赖于特定的框架或库
缺点:
-
浏览器和框架的支持不够:浏览器实现不一致,存在向后不兼容的版本问题,需要额外的polyfills支持
-
开发成本较高:需要重写现有的前端应用,在整个前端应用上把它们全部转换成Web Components
-
系统架构复杂:当应用被拆分为一个又一个的组件时,组件间通信需要额外设计
Shadow DOM
Shadow DOM 是一种浏览器的 Web 组件技术,是一种将HTML结构、样式和行为封装在一个独立的、封闭的DOM中的机制。它允许开发者将一个 Web 组件的样式和结构隐藏在组件作用域内,防止其与全局样式或逻辑发生冲突,创建一个封装的影子树(Shadow Tree),这个影子树对于外部是不可见的,它与组件的外部 DOM 树(也称为 Light DOM)是隔离的。在同一页面上运行多个互不干扰的前端环境。
-
原理:通过将HTML结构、样式和行为封装在一个独立的、封闭的DOM(即Shadow Tree)中,实现组件的封装和隔离,防止与全局样式或逻辑发生冲突。
优点:
-
封装性:提供了强大的封装能力,允许开发者将HTML结构、CSS样式和JavaScript代码封装在一个独立的DOM子树中,这些子树与主文档DOM树保持分离,从而保证了组件内部代码的隔离性和干净整洁
-
样式隔离:内部定义的CSS样式只在其作用域内有效,不会影响到外部文档,这有助于防止样式冲突,使得组件的样式更加独立和可控
-
简化组件开发:通过封装,开发者可以更容易地创建可复用的Web组件,而无需担心组件内部实现细节对外部的影响,这有助于提高开发效率和组件的可维护性
缺点:
-
兼容性:部分浏览器不完全支持
-
调试困难:由于Shadow DOM的封装性,内部DOM结构不易直接访问,增加调试复杂度
-
第三方集成限制:可能影响与第三方库或服务的集成,某些第三方库或扩展可能无法直接处理或解析Shadow DOM内容,因为它们不是为处理Shadow DOM而设计的,或者需要额外的配置才能与Shadow DOM兼容
iframe
iframe是一种HTML元素,允许在一个文档(父页面)中嵌入另一个文档(子页面),通过设置url 来做微应用的划分,使用 Iframe 可以将一个应用嵌入到另一个应用中,将不同的微前端应用程序嵌入到主应用程序的页面中,从而实现微前端的隔离和独立部署。在主应用中嵌入多个iframe,每个子应用在一个单独的iframe中运行,通过消息传递机制(如postMessage)进行通信。
它提供了浏览器原生的硬隔离方案,不论是样式隔离、js 隔离这类问题统统都能被完美解决,应用之间不会互相干扰。但最大问题也在于它的隔离性无法被突破,导致应用间上下文无法被共享,随之带来的开发体验、产品体验的问题,还存在如安全性和性能等方面的问题。
-
原理:每个子应用在一个单独的iframe中运行,通过消息传递机制(如postMessage API)进行通信,通过在主应用页面中嵌入iframe标签,并设置其src属性为子应用的URL,从而实现将多个独立的前端应用聚合到同一个页面中。
优点:
-
实现简单且隔离性强:iframe提供了天然的沙箱环境,web应用隔离的非常完美,无论是js、css、dom都完全隔离开来,互不影响
-
消息传递:只要每个iframe同源,可以使用Window. postMessageAPI来进行消息传递
缺点:
-
通信复杂且状态管理困难:iFrame间的通信方式(如postMessage、contentWindow访问或父子页面直接方法调用)相对繁琐,需制定复杂的通讯规范,,状态管理和公共依赖处理不如其他微前端方案高效,由于子父应用隔离,iframe 内外系统的通信、数据同步交互差,且URL状态不同步
-
全局上下文完全隔离,内存变量不共享:子应用切换时可能需要重新登录,体验不好,iframe 内外系统的通信、数据同步等需求,主应用的 cookie 要透传到根域名都不同的微应用中实现免登效果
-
路由和URL管理受限:路由状态不保活, iframe 的页面 url 中的状态信息并不能同步到父窗口,刷新iFrame会丢失其URL状态,无法使用浏览器的前进后退功能,需要配合存储解决
-
DOM隔离与交互限制:DOM结构不共享,iframe内的DOM与主页面完全隔离,限制了交互能力,iframe 的页面布局只针对于 iframe 窗口,如弹窗无法覆盖全局,弹窗只能在 iframe 内部展示,无法无法突破其本身覆盖全局,并且事件传递上存在者很大的问题,例如拖拽
-
性能问题:加载性能差,白屏时间长,不适合SPA应用,资源竞争可能影响页面加载速度,且每次加载都是新实例,整个应用全量资源加载,每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程,加载慢
-
样式和布局挑战:样式难以控制,全屏样式可能影响全局布局,UI不同步,iFrame内容独立,拥有自己的 CSS 样式和布局上下文,难以实现全局样式的一致性,以及子应用之间的布局和交互的协调
-
资源竞争:可能因资源竞争影响页面加载速度,iFrame与主页面共享连接池,而浏览器对相同域的连接有限制,因此会影响页面的并行加载,出现iframe中的资源占⽤了可用连接而阻塞了主页面的资源加载,Bundle的大小各异,构建时不能提取公共依赖关系
-
安全性与跨域限制:由于安全策略的限制,跨域iFrame通信受限,需处理复杂的安全策略问题
-
生命周期管理复杂:iframe的生命周期管理(如加载、渲染、卸载等)需要额外处理,处理不当可能影响用户体验
-
资源加载阻塞:iframe可能阻塞主页面的onload事件,影响页面加载效率
-
不利于SEO,预加载难以实现
基于服务端渲染的解决方案 SSR
服务端渲染可以将微前端应用程序的 HTML 和 JavaScript 在服务器端进行预处理,从而减少客户端的加载和渲染时间。这种解决方案可以提高性能和 SEO,但是也需要在服务器端增加额外的负载。
-
原理:在服务器端完成页面的HTML结构拼接处理,包括路由匹配、数据预取等,然后将生成的HTML页面直接发送给浏览器,再由浏览器进行展示。
优点:
-
性能优化:减少首屏加载时间,因为HTML内容已经在服务器上渲染完成,客户端只需接收和显示
-
SEO友好:搜索引擎爬虫能够直接获取到渲染后的HTML内容,有利于网站内容的索引和排名
-
更好的用户体验:对于依赖大量数据的页面,服务端渲染能够更快地显示关键内容,提升用户体验
-
代码共享:在某些情况下,服务端渲染可以更容易地实现代码共享,特别是当服务器和客户端使用相同或相似的技术栈时,这有助于减少代码重复和提高开发效率
缺点:
-
服务器资源消耗大:增加服务器的CPU和内存消耗,特别是在高流量情况下,需要更多的资源支持
-
开发复杂度增加:需要处理服务器和客户端之间的数据同步、状态管理等,开发门槛相对较高
-
缓存和CDN限制:动态生成的页面内容可能难以有效利用缓存和CDN,影响页面加载速度和可伸缩性
-
状态管理困难:在SSR中,由于服务器端和客户端都需要处理数据和状态,因此需要更加复杂的状态管理机制来确保数据的一致性和同步性
基于 JavaScript 模块加载器的解决方案
使用 JavaScript 模块加载器可以将不同的微前端应用程序作为独立的模块,在需要时动态加载这些模块,然后将渲染好的页面直接发送给客户端浏览器,从而减少了客户端的加载和渲染时间,实现微前端的隔离和独立部署。这种解决方案可以提高可维护性和扩展性,但是也需要使用特定的 JavaScript 模块加载器,如 SystemJS 或者 Webpack 等。
-
原理:将子应用分割成可动态加载的部分,主应用根据需要动态请求并执行子应用的代码。
优点:
-
按需加载和性能优化:减少加载时间,提升用户体验
-
易于扩展和维护:支持轻松添加或替换功能模块
-
支持多种模块规范:灵活选择适合项目的模块规范
缺点:
-
配置复杂性:使用模块加载器需要进行一定的配置工作,包括模块路径、依赖关系、加载顺序等
-
调试难度:由于模块之间的隔离和封装,当出现问题时可能需要跨模块进行调试,增加了调试的难度和复杂性
-
兼容性问题:不同的浏览器和JavaScript环境对模块加载器的支持程度可能不同,这可能会导致兼容性问题
-
模块间通信成本:模块间通信可能增加额外成本
基于微服务网关路由分发(路由分发式微前端)
通过HTTP服务器的反向代理功能或应用框架自带的路由机制,将不同的业务请求分发到对应的独立前端应用上,允许不同的子应用负责不同的路由空间,根据用户访问的路由动态加载对应的应用。
最常用的方案是通过 http服务的反向代理来实现。其实就是把一个大型项目拆分成多个微应用,每个微前端服务负责应用中的一个特定部分或功能,然后通过微服务网关(如Nginx、Zuul等)进行路由分发,确保用户请求能够正确转发到相应的前端服务,比如通过 Nginx 配置代理映射到不同的子应用上,这也叫路由分发式微前端。不同的子应用负责不同的路由空间,根据路由变化,加载对应的前端应用。
这种方式看上去更像是多个前端应用的聚合,即我们只是将这些不同的前端应用拼凑到一起,使他们看起来像是一个完整的整体。但是它们并不是,每次用户从 A 应用到 B 应用的时候,往往需要刷新一下页面。
http { server {listen 80;server_name xxx.xxx.com;location /api/ {proxy_pass http://localhost:4000/api;}location /web/admin {proxy_pass http://localhost:1000/api;}location / {proxy_pass /;} } }
通过上述配置,不同页面的请求就可以分发到不同的服务器上。
-
原理:通过微服务网关(如Nginx、Zuul等)的路由分发功能,将用户请求映射到不同的独立前端应用上,每个微前端服务负责处理特定路由的业务逻辑和界面渲染,实现应用的拆分、独立部署和按需加载。
优点:
-
实现简单、快速、易配置
-
不需要对现有应用进行改造,允许现有的前端应用作为微前端服务直接集成 缺点:
-
用户体验不好,在切换应用时会触发浏览器刷新,如果每个子应用都维护自己的会话状态,那么用户在切换子应用时可能需要重新登录
-
多个子应用无法并存,由于每个请求都直接映射到一个子应用,因此默认情况下,用户只能看到一个子应用的界面
-
应用状态管理困难,在多个微前端服务之间共享状态可能变得复杂,尤其是在没有统一状态管理方案的情况下
-
子应用之间的通信比较困难,可能会遇到跨域请求的问题
组合式应用路由分发(中心基座方案)
该方案的核心是主从思想,即包括一个基座(MainApp)应用和若干个微(MicroApp)应用。
① 基座应用:通常是一个前端SPA项目,负责整个系统的应用注册、路由映射、消息下发等核心功能。
② 微应用:独立前端项目,这些项目不限于采用 React,Vue,Angular 或者 JQuery 开发,每个微应用注册到基座应用中,由基座进行管理,也可以脱离基座单独访问。
-
原理:通过一个中心基座应用来管理多个独立的微应用,基座应用负责应用注册、路由映射和消息传递等核心功能,而微应用则以独立前端项目的形式存在,可以注册到基座中由基座统一管理,也可以独立运行。用户访问时,基座应用根据路由动态加载并渲染对应的微应用,实现应用的模块化、独立开发和部署。
基本的流程如下图所示:
优点:
-
技术不限制: 可以各自使用完全不同的前端框架
-
无感切换: 因为是一个 SPA 项目,所以切换时不会感觉到页面的刷新或重载,体验极佳
-
利于SEO,通过使用SPA框架和服务器端渲染(SSR)等技术,可以优化搜索引擎对内容的抓取和索引
-
独立开发与部署,降低了系统之间的耦合度,提高了开发效率
-
微前端优势几乎都有,包括资源复用、技术栈多样性、快速迭代等微前端架构的常见优势
缺点:
-
沙箱不隔离: 也就是 js 与 css 样式会出现冲突的问题
-
配置复杂:为了实现微应用之间的无缝集成和路由分发,基座应用需要进行复杂的配置,包括路由映射、资源加载、样式隔离等
-
性能考虑:虽然微前端架构可以提高资源的复用率,但在某些情况下,加载多个微应用可能会导致页面加载时间的增加
-
调试困难:由于微应用之间可能存在复杂的依赖和交互关系,因此在出现问题时,调试可能会变得比较困难
容器化方案
主要是通过容器化技术(如Docker)将前端应用及其所有依赖项打包成独立的容器镜像,并在容器环境中运行,开发者能够确保应用在不同环境中的一致性和可移植性。当容器被部署到任何支持容器技术的环境中时,它都能以相同的方式运行,无需担心环境差异带来的兼容性问题。这种方式允许前端应用以独立的服务形式存在,并通过API进行通信和集成。同时,也可以利用Webpack模块联邦等工具实现前端应用的模块化和共享。
-
原理:利用容器化技术(如Docker)封装前端应用及其环境,通过容器编排实现应用的独立、可移植、可扩展部署与管理。
优点:
-
提高应用的可移植性和可扩展性:容器化确保应用在不同环境中一致运行,并轻松应对负载增加
-
便于在多个环境中部署和运维:自动化工具支持容器化应用的快速部署、回滚和扩展,简化运维流程
-
促进前后端分离和微服务架构:容器化使前端应用独立运行,通过API与后端服务通信,促进模块化
缺点:
-
需要掌握容器化技术的相关知识:团队成员需学习Docker、Kubernetes等新技术和工具
-
可能增加系统的复杂性和运维成本:容器化引入新架构和运维挑战,需额外工具和流程支持
-
容器化可能不是所有情况的最佳选择:小型项目或团队可能因复杂度和成本考虑而不适合容器化
webpack5: Module Fedearation
webpack5 提供一个新特性 module fedaration(模块联邦),允许将JavaScript应用程序拆分成独立的模块,并在不同的Webpack构建中共享这些模块。可以帮助将多个独立的构建组成一个应用程序, 不同的构建可以独立开发与部署。允许每个模块(或子应用)暴露自己的入口点和共享模块给其他模块使用,同时也能够远程加载并执行其他模块暴露的入口点和模块。
基于这个特性,我们可以在一个 js 应用中动态加载并运行另一个js 应用的代码,在一个应用里动态渲染另一个应用的页面,并实现应用之间的依赖共享,这样也就实现了多个子应用的聚合。
-
原理:通过配置 Webpack 的 Module Federation 插件,可以将不同的应用打包成独立的模块,并在需要时从其他应用中动态加载这些模块。
优点:
-
开箱即用:只需要执行几行命令即可拉取相应的模块代码并把项目跑起来,包括基座应用和微前端应用,无需处理构建工具的复杂配置
-
独立开发与部署:基于提供的代理工具,在单独开发微应用时需启动基座或者其它微应用,应用启动后,无需加载与自己无关的资源
-
去中心化,子应用依赖解耦:通过模块共享的方式避免了传统微前端架构中的中心基座依赖,使得系统更加灵活和可扩展
-
相同资源不需要重复加载:组件共享,与npm发包类似的组件共享管理,相同的资源在多个应用间共享,避免了重复加载
-
不需要对原有应用进行改造,主要集中于打包脚本的调整,对应用代码的影响较小
-
切换应用时,浏览器不用重载页面,提供和单页应用一样的用户体验
-
多个子应用可并存(通过动态加载的方式,支持在运行时动态加载和执行远程应用的代码,实现了应用的灵活聚合)
缺点:
-
无法沙箱隔离:没有有效的CSS沙箱和JavaScript沙箱,需借助其它工具和框架才能做到应用层面的隔离
-
对Webpack强依赖,老旧项目不友好:构建工具只能使用 webpack5 版本以上,对已有项目使用旧版本 Webpack 的情况不友好,需进行升级和改造
-
代码封闭性高:依旧需要做npm那一套管理和额外的拉取代码,需要仔细管理不同模块之间的依赖关系,以避免版本冲突
-
拆分粒度需要权衡:共享的lib无法做到tree-shaking,可能引入不必要的代码
-
依赖前置:可能需要预加载一些依赖,导致时间加载变长
-
主、子应用的路由可能发生冲突
二、微前端框架
single-spa
Single-SPA管理一个主路由,根据路由的变化动态地加载和卸载子应用。每个子应用都是独立的,拥有自己的路由和生命周期,但在主应用的上下文中运行。
一个将多个单页面应用聚合为一个整体应用的 JavaScript 微前端框架,实际上就是一个聚合,使用这个库可以让应用使用多个不同的技术栈进行同步开发,最后使用一个公用的路由去实现完美的切换。在single-spa方案中,应用被分为两类:基座应用和子应用。其中,子应用就是需要聚合的子应用;而基座应用,是另外一个单独的应用,用于聚合子应用。采用的是 JS entry,它通过监听 URL 变化并在路由级别管理各个子应用的加载、挂载和卸载过程。子应用需要暴露特定的生命周期钩子函数(如 bootstrap、mount 和 unmount)以便与 single-spa 框架集成。
single-spa实现微前端的整体流程:
资源模块加载器:专为加载子项目初始化资源设计的。它将子项目的入口JavaScript文件构建为UMD格式,并利用模块加载器(如SystemJs,但非必须)远程加载这些资源。
子应用资源配置表:负责存储每个子应用入口资源的URL,确保在切换子应用时,能够迅速通过加载器加载到最新版本。由于子应用更新会改变资源文件的hash值,服务端需定期更新此配置表,以保证框架加载的是最新资源。
值得注意的是,single-spa原生不支持直接管理子应用资源列表。每个子应用需将其所有初始化资源打包至单一入口文件。若子应用初始化资源分散于多个文件,可利用webpack-manifest-plugin等工具生成资源清单,并据此进行额外处理,实现多文件资源的有效加载。
核心原理: 在 基座 (主) 应用 中注册所有 App 的路由,single-spa 保存各子应用的路由映射关系,充当微前端控制器 Controler,当对应的 URL 变换时,除了匹配基座应用本身的路由外,还会匹配子应用路由并加载渲染子应用。single-spa是一个顶层路由,当路由处于活动状态时,它会下载并执行该路由的相关代码。通过劫持路由的方式来做子应用之间的切换,但接入方式需要融合自身的路由,有一定的局限性。
和单页应用的实现原理类似,single-spa 会在基座应用中维护一个路由注册表,每个路由对应一个子应用。基座应用启动后,当我们切换路由时,如果是一个新的子应用,会动态获取子应用的js脚本,然后执行脚本并渲染出相应的页面;如果是一个访问过的子应用,那么就会从缓存中获取已经缓存的子应用,激活子应用并渲染出对应的页面。(核心思想: 路由劫持 === >应用加载)
优点:
-
在同一页面上使用多个前端框架而不用刷新页面
-
独立开发、独立部署每一个单页面应用
-
新功能使用新框架,旧的单页应用不用重写可以共存
-
改善初始加载时间,延迟加载代码
缺点:
-
不支持Js沙箱、样式隔离,容易会出现Js冲突,样式污染,window对象的穿透问题等
-
不支持元素隔离、预加载等
-
路由管理复杂:需要仔细设计路由规则,以确保子应用之间的路由不冲突
-
无通信机制
-
状态管理挑战:由于子应用是独立运行的,因此状态管理(如全局状态)需要额外的考虑和设计
-
只支持 JS entry,限制了它只能支持 vue 、 react 、 angular 等技术开发的项目,对一些 jQuery 老项目则无能为力
-
调试和监控难度增加:随着应用规模的扩大,调试和监控多个子应用可能变得更加复杂
single-spa
是通过监听url change
事件,在路由变化时匹配到渲染的子应用并进行渲染,这个思路也是目前实现微前端的主流方式。同时single-spa
要求子应用修改渲染逻辑并暴露出三个方法:bootstrap
、mount
、unmount
,分别对应初始化、渲染和卸载,这也导致子应用需要对入口文件进行修改。因为qiankun
是基于single-spa
进行封装的,所以这些特点也被qiankui
继承下来,并且需要对webpack
配置进行一些修改。
qiankun
Qiankun 在 single-spa 的基础上,增加了应用加载、沙箱隔离、资源预加载等特性,使得微前端的实现更加简单和高效。
qiankun(阿里蚂蚁金服)是一个基于 single-spa 进行二次开发的微前端框架,旨在提供更简单、无痛的构建一个生产可用微前端架构系统。和 single-spa 一样,qiankun 也能提供类似单页应用的用户体验,同时也支持按需加载和运行时动态注入资源,具有较高的灵活性和扩展性。可以认为是由single-spa和import-html-entry两个库结合,并进行二次开发的产物,在框架层面解决了使用single-spa 时需要开发人员自己编写子应用加载、通信、隔离等逻辑的问题。通过 import-html-entry 包解析 HTML 获取资源路径,然后对资源进行解析、加载。
子应用加载:使用的是HTML Entry,通过动态创建 script 标签的方式加载子应用的入口文件,加载完成后,会执行子应用暴露出的生命周期函数
JS沙箱:使用的是ProxySandbox,通过 Proxy 对象创建了一个 JavaScript 沙箱,用于隔离子应用的全局变量,防止子应用之间的全局变量污染
CSS沙箱:有两种方式,一种是严格样式隔离,通过 shadow dom 来实现,另一种是实验性的样式隔离,就是 scoped css,两种方式不可共存。通过动态添加和移除样式标签的方式实现样式隔离,当子应用启动时,会动态添加子应用的样式标签,当子应用卸载时,会移除子应用的样式标签
父子应用通信:使用的是GlobalState,原理其实就是上面提到的全局公共BUS, 提供了一个全局的通信机制,允许子应用之间进行通信
基于路由的动态加载:qiankun的路由系统可以实现根据路由动态加载子应用,在用户访问不同的页面时,qiankun会根据当前页面的路由信息动态加载相应的子应用,从而实现多个子应用的集成
增加资源预加载能力,预先子应用html、js、css资源缓存下来,加快子应用的打开速度
子应用 dom 结构如下:
优点:
-
HTML entry 及沙箱的设计使得接入微应用像使用 iframe 一样简单,既支持 JS entry,又支持 HTML entry,不必像 single-spa 那样需要手动梳理资源链接
-
切换应用时,浏览器不用重载页面,提供和单页面一样的用户体验,监听路由自动的加载、卸载当前路由对应的子应用
-
路由保持,浏览器刷新、前进、后退,都可以作用到子应用
-
相比single-spa,解决了子应用加载、应用隔离、子应用通信等问题,通过 Proxy 和 iframe 等技术实现了样式隔离和脚本隔离,避免了子应用之间的冲突
-
丰富的插件系统:支持通过插件扩展功能,如应用间的通信、状态共享等,提供了更加开箱即用的 API
-
资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度
-
多个子应用可并存,应用间通信简单,全局注入
缺点:
-
子应用接入成本较高,需要对原有应用进行改造,应用要兼容接入qiankun和独立使用,从 webpack、代码、路由等等都要做一系列的适配
-
可能对一些 jQuery 老项目支持性不是特别好
-
相同资源重复加载
-
启动应用时,要先启动基座应用
-
无法支持 vite 等 ESM 脚本运行
-
基于路由匹配,无法同时激活多个子应用,也不支持子应用保活
-
css 沙箱无法绝对的隔离,js 沙箱在某些场景下执行性能下降严重
wujie
wujie(腾讯)是一款基于 Web Components + iframe 的微前端架,很好地解决了Iframe做沙箱的痛点,简单说,无界的方案就是:JS 放 iframe 里运行,DOM 放 webComponent 渲染。
在应用 A 中构造一个shadowRoot 和iframe,然后将应用 B 的html写入shadowRoot中,js运行在iframe中,因为iframe的js隔离真的很完美。即:子应用加载在一个shadowRoot里,JS在iframe中执行并代理到这个shadowRoot上。
子应用加载:Htmlentry + 渲染到shadowRoot上
创建了一个标签名为wujie-app的Webcomponent,JS、CSS加载。
JS沙箱
基本原理:直接把js丢在一个隐藏的iframe里执行
细节:在iframe中拦截document对象,统一将dom指向shadowRoot,此时比如新建元素、弹窗或者冒泡组件就可以正常约束在shadowRoot内部。
CSS沙箱
基于上面说的shadowRoot
父子应用通信
Props 注入
子应用通过 $wujie.props 可以拿到主应用注入的数据
window.parent 通信
window.document.querySelector("iframe[name=子应用id]").contentWindow.xxx// 子应用调用 window.parent.xxx
去中心化的通信
上面提到的EventBus
优点:
-
极速、应用保活
-
开箱即用,相比 qiankun 接入成本更低
-
子应用理论上不需要做任何改造
-
iframe 沙箱隔离性好、原生性能
-
vite 兼容性好
缺点:
-
需要时间学习、坑比较多
-
对于不支持 webcompnent 的浏览器没有做降级处理
-
长期维护性一般、内存开销较大
-
目前还比较新,社区相对不够活跃
micro-app
micro-app (京东)是一个基于 Web Components 的前端微服务框架,支持多种前端框架。它是目前市面上接入微前端成本最低的框架,并且提供了 JS沙箱、样式隔离、元素隔离、预加载、资源地址补全、插件系统、数据通信 等一系列完善的功能。它的特点就是取各家所长,最终成为了功能最丰富的微前端框架。
样式隔离方案与 qiankun 的实验方案类似,也是在运行时给子应用中所有的样式规则增加一个特殊标识来限定 css 作用范围。
子应用路由同步方案与 wujie 类似,也是通过劫持路由跳转方法,同步记录到 url 的 query 中,刷新时读取并恢复。
组件化的使用方式与 wujie 方案类似,这也是 micro-app 主打的宣传点。
它内置了两种沙箱:①类 qiankun 的 with 代理沙箱,据说相比 qiankun 性能高点,但目前微前端框架界并没有一个权威的基准性能测试依据,所以并无有效依据支撑。②类 wujie 的 iframe 沙箱,用于兼容 vite 场景。开发者可以根据自身的实际情况自由选择。
优点:
-
兼容所有框架,支持的功能最丰富:为了保证各个业务之间独立开发、独立部署的能力,micro-app做了诸多兼容,在任何技术框架中都可以正常运行
-
接入成本低、零依赖、使用简单:将所有功能都封装到一个类WebComponent组件内,从而实现在基座应用中嵌入一行代码即可渲染一个微前端应用
-
开箱即用:提供了is沙箱、样式隔离、元素隔离、预加载、数据通信、静态资源补全、插件系统等一系列完善的功能
缺点:
-
功能丰富导致配置项与 api 太多
-
对于不支持 webcompnent 的浏览器没有做降级处理
-
静态资源补全问题:静态资源补全是基于父应用的,而非子应用则需要开发者自己手动解决
-
0.x版本 vite支持不是很好,使用的时候需要关闭沙箱;1.x版本 支持vite,需要采用iframe沙箱模式,这点和wujie的方案一样了,都是webComponent + iframe
借鉴了
WebComponent
的思想,通过CustomElement
结合自定义的ShadowDom
,将微前端封装成一个类WebComponent
组件,从而实现微前端的组件化渲染。并且由于自定义ShadowDom
的隔离特性,micro-app
不需要像single-spa
和qiankun
一样要求子应用修改渲染逻辑并暴露出方法,也不需要修改webpack
配置,是目前市面上接入微前端成本最低的方案。
EMP
EMP(欢聚时代)是一款基于Webpack5 Module Federation构建的微前端解决方案。与其他微前端方案相比,它在处理跨框架和状态管理时可能面临一定的复杂性。然而,EMP的主要优势在于其强大的项目解耦能力,而非专注于多系统的直接聚合。这一特点使得EMP更接近于像bit这样的跨项目组件复用平台,专注于提升组件和模块的可重用性与独立性。 优点:
-
依赖自动管理,可以共享 Host 中的依赖,版本不满足要求时自动 fallback 到 Remote 中依赖
-
共享模块粒度自由掌控,小到一个单独组件,大到一个完整应用。既实现了组件级别的复用,又实现了微服务的基本功能
-
共享模块非常灵活,模块中所有组件都可以通过异步加载调用
缺点:
-
无法做到多框架兼容等微前端方案的痛点
-
基于 Webpack5 Module Federation,需要统一 Webpack5 技术
-
文档资料,社区不够活跃
Garfish
Garfish(字节跳动)旨在应对现代Web应用面临的两大挑战:前端技术生态的繁荣与Web应用本身的复杂化。它有效解决了跨团队协作的难题,支持多样化的技术体系,并简化了复杂应用的开发与管理。经过大量线上应用的实践验证,Garfish功能稳定可靠,为开发团队提供了强大的支持。 优点:
-
高效预加载:Garfish 强大预加载机制,根据用户习惯智能加载,显著缩短应用切换时间
-
依赖共享:支持依赖共享,减少重复加载,大幅降低整体包体积
-
数据监控:内置数据收集功能,实时监控应用运行状态,助力问题排查与性能优化
-
多实例运行:支持多实例同时运行,增强业务拆分灵活性,满足复杂场景需求
-
灵活接入:通过 Loader 支持多种入口方式(HTML/JS),简化微前端应用接入流程
-
路由隔离:Router 模块实现路由驱动与主子路由隔离,简化配置,自动渲染与销毁
-
运行时隔离:Sandbox 模块提供运行时隔离,避免 JS、CSS 副作用影响,确保应用稳定
缺点:
-
架构复杂:增加系统复杂性,维护成本上升
-
技术栈限制:可能不完全兼容所有技术栈,集成难度大
-
性能调优:需要细致的性能调优,子应用增多可能影响性能
-
调试困难:问题定位复杂,需了解整个系统架构
-
学习成本:不熟悉微前端架构的开发者需投入时间学习
-
社区支持有限:开源社区活跃度和文档可能不足
Bit
Bit 是一款由国际开发团队开源的跨项目组件复用平台,它专注于将独立的组件进行构建、封装,并集中管理,以便在不同项目间轻松集成和复用这些组件。这一平台极大地提高了组件的可重用性和开发效率。 优点:
-
安全性与健壮性:继承了传统单体式前端项目的安全性和健壮性特性,确保组件的可靠运行
-
易于接入与高度可伸缩:提供简单的介入方式,同时支持高度的可伸缩性,满足不同规模项目的需求
-
优化工作流程:通过解耦代码库、鼓励自治团队、定义清晰的小型API、建立独立的发布流程以及支持持续增量升级,增强了开发工作流程的效率与灵活性
-
适用于组件化开发:严格意义上讲,Bit 与微前端在某些方面存在差异,因此它更适合那些以组件为核心开发模式,且技术栈相对统一的项目,有助于实现高效的组件复用和项目管理
缺点:
-
技术栈限制:可能最适合技术栈较为统一的项目,对于技术栈多样化的项目可能兼容性较差
-
学习曲线:对于新使用者来说,需要一定时间来熟悉其组件管理流程和工具链
-
社区与文档:相比一些主流框架,其社区活跃度和文档丰富度可能稍逊一筹
参考资料:
一个写给初学者如何搞微前端的「从入门到放弃专栏」 - 掘金 (juejin.cn)
聊聊微前端的原理和实践-腾讯云开发者社区-腾讯云 (tencent.com)