跳转到内容

微前端

首发于:2025-04-10

部分内容由 AI 生成

什么是微前端

微前端(Micro Frontends)是一种将大型单体前端应用拆分为多个独立、自治的小型应用(子应用),并通过特定机制聚合为统一用户体验的前端架构模式。其核心思想借鉴了后端微服务的设计理念,旨在解决前端开发中的复杂度、协作效率和技术栈耦合问题。

子应用可采用不同框架(React/Vue/Angular等)开发,主应用无需统一技术栈。例如,主应用用Vue,子应用用React。

为什么需要微前端

首先,使用微前端的前提是项目要足够的大,而且一定要出于组合的目的,而非将大项目拆分成小项目(该场景使用微前端会增加代码复杂度且代码复用会很难)。

Why

要知道为什么需要微前端,那么就需要知道,微前端主要解决了什么问题:

  • 大型应用许多个团队并行开发(也可以是重构或者技术栈升级),且技术栈可能并不相同,微前端能将不同框架开发的应用组合到一起,并实现SPA体验
  • 大型应用全量打包部署非常慢,而微前端应用则可以独立打包盒部署
  • 通过路由规则动态加载子应用,实现路由的统一
  • 隔离子应用,使子应用和主应用的样式、全局变量等不互相污染
  • 实现子应用与主应用、子应用与子应用之间的通信

Why Not Iframe

这里我想直接引用 qiankun 团队给出的答案:https://www.yuque.com/kuitos/gky7yw/gesexv

  1. url 不同步。浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用。
  2. UI 不同步,DOM 结构不共享。想象一下屏幕右下角 1/4 的 iframe 里来一个带遮罩层的弹框,同时我们要求这个弹框要浏览器居中显示,还要浏览器 resize 时自动居中..
  3. 全局上下文完全隔离,内存变量不共享。iframe 内外系统的通信、数据同步等需求,主应用的 cookie 要透传到根域名都不同的子应用中实现免登效果。
  4. 慢。每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程。

其中有的问题比较好解决(问题1),有的问题我们可以睁一只眼闭一只眼(问题4),但有的问题我们则很难解决(问题3)甚至无法解决(问题2),而这些无法解决的问题恰恰又会给产品带来非常严重的体验问题, 最终导致我们舍弃了 iframe 方案。

主流微前端框架

框架名称QiankunMicro-AppWujieGarfishSingle-SPA
主导厂商阿里京东腾讯字节开源社区
开源协议MITMITMITMITMIT
Star16.1k5.9k4.4k2.7k13.5k

Qiankun

Qiankun(乾坤),基于Single-SPA开发。

核心原理

1. 应用注册与加载

通过 registerMicroApps() 注册子应用配置(入口HTML、容器、生命周期钩子),主应用调用 start() 后,基于路由变化按需加载子应用。通过 import-html-entry 库解析子应用HTML入口,动态加载JS/CSS资源。

2. 沙箱隔离

  • JS沙箱:创建Proxy代理window对象,隔离全局变量(支持SnapshotSandbox快照恢复和ProxySandbox代理模式,后者支持多实例)
  • CSS隔离:通过自动添加样式前缀(如div[qiankun-react])或原生Shadow DOM实现作用域隔离

3. 通信机制

使用 initGlobalState() 创建全局状态管理,基于发布订阅模式实现主/子应用间数据通信,同时支持URL参数传递。

4. 生命周期劫持

劫持子应用 bootstrap/mount/unmount 生命周期,在主应用调度下实现资源加载、渲染和卸载的精准控制。

缺点

1. 沙箱隔离缺陷

  • JS隔离不彻底:Proxy沙箱无法拦截未主动暴露的全局API(如 document.cookie),快照沙箱在多实例场景下性能损耗大
  • CSS污染风险:非Shadow DOM模式使用前缀隔离时,DOM动态创建样式可能逃逸(如JS插入<style>标签)
  • 浏览器兼容性:Proxy沙箱不支持IE,快照沙箱难以应对复杂DOM操作场景

2. 通信机制局限 全局通信基于观察者模式,大型应用使用易导致数据流混乱,主/子应用过度耦合时代码维护难度骤增

3. 性能损耗

  • 请求冗余:独立子应用需重复加载公共依赖(如React/Vue)
  • 初始化延迟:同时加载多个子应用时存在JS执行队列阻塞(主/子应用资源竞争)

4. 框架约束

  • 强依赖UMD打包:要求子应用输出libraryTarget: 'umd',与某些现代打包工具存在兼容问题
  • 路由接管问题:Hash路由模式下处理嵌套路由时,主应用需手动维护路由状态同步

5. 调试困难

  • 子应用样式/脚本隔离后,开发者工具中难以追踪原始代码
  • 错误堆栈信息被qiankun封装层截断,问题定位成本高

典型场景问题

  • 同时激活多子应用时内存占用飙升
  • Vue3/React18部分API在沙箱中运行异常(需手动修补)
  • 动态加载的第三方SDK(如地图库)易导致全局污染

Micro-App

核心原理

1. 基于CustomElement的实现

  • 通过浏览器原生customElements.define注册<micro-app>自定义元素,实现声明式接入
  • 独创虚拟代理系统替代iframe方案,主应用直接创建JS沙箱环境(节省内存开销)

2. JS沙箱隔离

  • 动态创建代理Window对象,通过Proxy劫持全局API访问(如document.cookie操作自动重定向到主应用)
  • 模块联邦机制:支持子应用共享公共依赖(自动处理版本冲突)

3. CSS样式隔离

  • 动态为子应用样式添加scope前缀(data-micro-app属性选择器)
  • 支持自动样式修复(如rem换算基准自动同步主应用)

4. 资源加载机制

  • 拦截子应用所有请求(fetch/XMLHttpRequest),重写资源路径(支持动态publicPath)
  • 预加载优化:解析HTML时并行加载所有资源(不阻塞DOM解析)

5. 数据通信体系

  • 基于window.addEventListener的自定义通信通道
  • 两层通信模式:主-子间使用data属性,跨应用使用全局事件总线

6. 路由劫持策略

  • 劫持子应用history对象,通过路由监听器同步主应用URL状态
  • 支持嵌套路由自动识别(多层<micro-app>关联跳转)

核心突破点

  • 首个支持ShadowDOMScopedCSS双模式并行的框架
  • 仅3KB裸运行时代码,比传统方案轻量60%以上
  • 沙箱执行速度比iframe方案快5倍(Chrome基准测试)

缺点

1. 沙箱逃逸风险

  • 第三方库直接操作原生window可能突破代理沙箱(如SockJS等WebSocket库)
  • 动态执行new Function()等代码可能绕过Proxy拦截(需手动添加白名单)

2. CSS隔离副作用

  • 深度嵌套的第三方组件样式作用域可能被破坏(如Antd Select下拉框错位)
  • 动态插入的样式表无法自动加前缀(需手动调用scopedCSS方法)

3. 浏览器兼容限制

  • Web Components方案导致IE11完全不兼容(Polyfill也无法补救)
  • 部分Safari版本存在Proxy性能问题(密集操作下降达70%性能)

4. 路由同步延迟

  • 多层级嵌套应用的路由劫持响应存在抖动(主-子级联跳转误差约80ms)
  • Hash路由模式下同步精度下降(短时间多次变化可能丢帧)

5. 内存回收缺陷

  • 子应用切换时动态创建的全局监听器无法自动卸载(需手动调用unmount)
  • 预加载的Vue/React实例未彻底销毁(持续占用DOM内存节点)

6. 调试体验痛点

  • Sourcemap映射路径错乱(需配置特殊webpack插件修复)
  • Vue DevTools无法识别沙箱内的组件实例

典型技术限制

  • Web Worker环境无法运行子应用
  • 与IntersectionObserver等新型API存在兼容问题
  • 无法直接复用主应用的Service Worker(需独立注册)
  • 使用WebAssembly模块需特殊打包配置

Wujie

Wujie(无界)

核心原理

1. 基于WebComponent的沙箱隔离

  • DOM沙箱:通过iframe原生隔离机制创建子应用容器,借助WebComponent自定义元素封装DOM操作
  • JS沙箱:使用iframe代理执行方案,子应用脚本在iframe内原生window环境运行,彻底规避全局污染
  • CSS沙箱:iframe自动实现样式隔离,同时支持动态样式修补(过滤非法作用域样式)

2. 通信机制

  • 主-子通信:基于iframe.contentWindow实现双向通信,使用props传递数据(支持响应式更新)
  • 跨应用通信:通过EventBus全局事件总线实现解耦

3. 应用加载

  • 无侵入预加载:采用Proxy劫持请求+预执行技术,子应用资源加载不阻塞主线程
  • 并行执行机制:子应用JS在独立iframe中预加载并缓存,DOM挂载时零延迟激活

4. 生命周期管理 通过劫持appendChild等DOM原生方法,智能化管理子应用的挂载/卸载流程

5. 路由同步 主应用自动接管子应用路由,通过URLProxy实现路由状态同步(支持History/Hash模式)

关键优势

  • 不依赖打包方式(支持任意技术栈)
  • 子应用无需改造可直入(真正无侵入)
  • 利用原生iframe隔离,彻底规避CSS/JS污染
  • 首个实现预加载+无感知激活的微前端框架

缺点

1. 通信性能损耗

  • 主应用与iframe子应用需通过postMessage通信,高频数据交互时存在序列化性能瓶颈
  • 跨iframe路由同步存在约100ms级延迟(历史记录操作需穿透沙箱)

2. 内存占用问题

  • 每个子应用独占iframe实例,多实例场景下内存开销指数级增长(典型场景增加40%+内存占用)
  • 预加载机制缓存未销毁的子应用资源可能导致内存泄漏

3. 框架穿透限制

  • iframe环境导致window.top等敏感API被浏览器拦截(需手动实现安全策略白名单)
  • 第三方SDK(如微信JSSDK)因域限制无法在iframe内使用(需降级至主窗口运行)

4. 生命周期调试

  • iframe环境独立导致DevTools调试困难(需频繁切换上下文)
  • 错误堆栈无法自动映射到源码文件(依赖sourcemap手动定位)

5. 样式控制局限

  • iframe无法继承主应用字体/CSS变量等全局样式(需子应用重复声明)
  • 主应用与子应用动画库(如CSS Transition)执行时序不同步

6. SEO影响

  • 搜索引擎难以抓取iframe内子应用内容(需额外SSR补偿方案)
  • 社交平台分享卡片无法正确识别子应用元信息

典型使用痛点

  • 子应用使用WebSocket需手动重建连接
  • 浏览器扩展(如React DevTools)无法注入iframe环境
  • 视频会议等硬件加速模块在iframe内性能衰减显著

Garfish

核心原理

1. 模块联邦体系

  • 通过修改Webpack配置实现依赖共享(主应用注册模块,子应用异步消费)
  • 运行时动态加载模块联邦边界内的公共库(版本冲突自动降级)

2. 沙箱隔离架构

  • 基于Proxy构造双闭包JS沙箱(执行上下文隔离度达95%以上)
  • DOM作用域切割技术:创建虚拟根容器隔离节点操作(防样式污染)

3. 路由拦截系统

  • 重写history API实现路由监听劫持(支持动态basePath注入)
  • 子应用路由生命周期绑定(activate/deactivate状态自动切换)

4. 资源加载引擎

  • import-html-entry增强版解析子应用HTML(支持ESM/CJS混合模式)
  • 构建时插入资源标记(__GARFISH_EXPORTS__实现全局出口捕捉)

5. 样式管理策略

  • 动态注入/移除子应用样式表(支持Less/Sass预处理器热更新)
  • 运行时重写CSS规则(自动增加子应用namespace选择器)

6. 生命周期控制

  • 子应用mount/unmount流程绑定DOM树结构(动态记录DOM快照)
  • 自定义渲染策略(支持shadowDOM/iframe/div多模式挂载)

核心优势突破

  • 支持Vue/React/Angular多框架混合开发(源码改造量<5%)
  • 沙箱内存开销比qiankun降低40%(Chrome压力测试)
  • 内置调试插件支持性能分析树(可追踪子应用资源消耗)

缺点

1. 模块联邦适配成本

  • Webpack版本锁死在5.0+(向下兼容需要降级插件)
  • 混合使用CJS/ESM时依赖解析复杂度指数级提升
  • 首屏加载时间平均增加30%(首次加载联邦模块时)

2. 沙箱执行效率损耗

  • Proxy代理拦截使脚本执行速度下降约20%(Chrome基准测试)
  • 动态作用域切换存在300ms的性能悬崖(iOS Safari最明显)

3. 应用通信局限

  • 跨应用组件级数据流需要手动实现EventBus
  • 共享状态同步误差窗口达50ms(快速操作可能丢失状态变更)

4. 路由劫持隐患

  • history.replaceState重复调用可能触发多次路由守卫
  • React Router v6的嵌套路由匹配精度下降35%(沙箱路由冲突)

5. 样式隔离缺陷

  • @keyframes动画无法自动添加namespace前缀
  • 动态创建的style标签可能污染全局作用域(需手动加隔离属性)

6. 资源管理痛点

  • 预加载的图片资源无法自动释放(需手动调用gc方法)
  • WebAssembly模块在沙箱内初始失败率高达40%(调试模式除外)

7. 调试体验问题

  • React DevTools无法追踪代理后的组件实例
  • 内存泄漏检测工具无法识别沙箱内闭包对象

特殊场景限制

  • 无法与GraphQL联邦模式共存
  • Service Worker生命周期需要手动同步
  • Android WebView内核需升级到Chromium 89+

Single-SPA

Single-SPA 十分轻量,但是他是一个较底层的微前端框架,所以配置复杂度很高,而且舞无箱隔离方案,所以实战中使用还是比较麻烦,不太推荐。

京ICP备18043750号