Qiankun 微前端框架学习笔记

Qiankun 是目前业界比较成熟的一套微前端框架方案,最早由阿里内部衍生并开源。它本质上基于 single-spa 的微前端模型进行了一层封装,提供了更完备的沙箱、预加载以及零配置集成体验等特性。在微前端架构中,主应用(基座)和子应用通常需要在同一个页面中协作与运行,而 Qiankun 核心解决的正是如何优雅地加载与卸载子应用、同时又保证子应用间的隔离(如全局变量、样式等),并且在主应用和子应用之间实现通信和数据传递。

Qiankun 微前端框架学习笔记

概览

Qiankun 是目前业界比较成熟的一套微前端框架方案,最早由阿里内部衍生并开源。它本质上基于 single-spa 的微前端模型进行了一层封装,提供了更完备的沙箱、预加载以及零配置集成体验等特性。在微前端架构中,主应用(基座)和子应用通常需要在同一个页面中协作与运行,而 Qiankun 核心解决的正是如何优雅地加载与卸载子应用、同时又保证子应用间的隔离(如全局变量、样式等),并且在主应用和子应用之间实现通信数据传递

下面将从以下几个维度对 Qiankun 进行分析和解读:

  1. 微前端的背景与核心需求
  2. Qiankun 的总体设计思路
  3. 沙箱与隔离机制
  4. 路由与生命周期管理
  5. 子应用加载与资源处理
  6. 数据通信与共享
  7. 预加载和性能优化
  8. 与 single-spa 的关系
  9. 总结与思考

微前端背景与核心需求

传统单体应用(Monolithic Application)在规模不断扩张时,常常面临以下问题:

  • 复杂度提升:多人协作时,代码耦合度高、难以维护,发布节奏不同步。
  • 技术栈难以统一升级:当旧项目需要演进到新技术栈时,往往需要大规模重构。
  • 部署/版本冲突:一次部署往往影响整个应用,即使只改动一小部分,也需要整个应用回归测试。

微前端思想与微服务类似,将一个大型前端应用拆分成若干相对独立的子应用(子模块),各子应用可以:

  • 独立开发/构建/部署
  • 独立使用各自的技术栈(React/Vue/Angular 等)
  • 独立维护自己的版本演进和部署节奏

微前端的核心需求可以总结为:

  1. 独立加载与运行:不同子应用互不干扰,能被主应用动态挂载和卸载。
  2. 样式与全局作用域隔离:解决冲突,避免修改共享的 window、document、样式等。
  3. 统一的路由与导航管理:主应用需要统一管理页面路径,以实现前端单页应用(SPA)的用户体验。
  4. 数据共享与通信:支持在主应用和子应用、或子应用之间传递和共享数据。

Qiankun 的总体设计思路

Qiankun 的设计理念可以用一句话概括:在简单的开发体验基础上,为子应用提供基于沙箱的运行和隔离能力,并且最大程度地保证与 single-spa 的兼容

主要体现为以下几点:

  1. 易用性:在使用上,Qiankun 提供了诸如 registerMicroAppsstart 等较高层的 API,开发者只需进行简单注册和配置即可快速让多个子应用接入。
  2. 沙箱机制:是 Qiankun 的核心价值所在,能够保证多个子应用在同一个页面中运行时,能尽可能隔离全局变量和样式污染。
  3. 基于生命周期的管理:借助 single-spa 的理念,子应用具有统一的 bootstrapmountunmount 等钩子函数,易于集中管理,也方便应用做各种初始化和销毁逻辑。
  4. 预加载与性能优化:Qiankun 支持在空闲时预先下载子应用静态资源,提高子应用首次打开时的速度体验。
  5. 技术栈无关:理论上,不管子应用是 React、Vue 还是 Angular,都可以通过特定的包装方式接入 Qiankun。

沙箱与隔离机制

为什么需要沙箱?

在单页应用中,如果同时加载多个子应用,它们都运行在同一个 window 上,各自的脚本、样式、全局变量、事件监听都有可能互相干扰。尤其当子应用数量或规模增多之后,这种全局作用域共享会导致难以排查的冲突,给调试和维护带来极大困扰。沙箱机制就是为了解决此类问题,通过“劫持”或“代理”全局变量访问,在应用被挂载时将访问作用域限制在应用自身需要的范围。

Qiankun 的沙箱类型

Qiankun 主要有两种沙箱实现机制(也会根据浏览器能力进行降级):

  1. Proxy 沙箱(一般称为 singular 模式和 legacy 模式)
    • 基于 ES6 的 Proxy 对象实现,通过 Proxy 代理 window 上的访问和修改操作,在子应用内部读写全局变量时,会映射到一份独立的内存空间。
    • 只要浏览器支持 Proxy,就能利用它实现动态沙箱,在不同子应用切换时关闭/重置沙箱。
    • 当子应用被卸载后,可以快速释放或重置相关全局状态。
  2. 快照沙箱(仅在不支持 ES6 Proxy 的老旧环境下使用)
    • 通过在子应用挂载前后“快照”全局对象,并在卸载时进行对比和还原,间接实现沙箱效果。
    • 由于快照沙箱需要对比 window 的所有改动,性能和准确性不如 Proxy 沙箱好,因而仅作为降级方案。

沙箱的实现原理

以 Proxy 沙箱为例,当我们加载某个子应用时,Qiankun 会:

  1. 创建一个 proxy 对象用于代理 window 的属性访问,内部有一个 record 用于存储被子应用修改的全局变量。
  2. 读操作:如果子应用在执行时访问某个全局变量 window.xxx,先检查沙箱自身记录是否有,如果没有则再去访问真实的 window,保证对内建 API 的获取不受影响。
  3. 写操作:若子应用去设置 window.xxx = ...,则实际上会写进沙箱内部的 record,而不污染外部真实的 window。
  4. 当该子应用被卸载或切换到下一个子应用时,可以禁用当前沙箱,后续对沙箱的写操作将失效,实现彻底的子应用隔离。

路由与生命周期管理

Qiankun 核心使用了 single-spa 的理念:基于路由进行应用激活,并在激活时调用子应用的生命周期函数

  1. 注册子应用:通过 registerMicroApps(apps, lifeCycles?) 配置子应用信息,每个子应用包括:
    • name:应用名称
    • entry:应用入口(HTML 或 JS 地址)
    • activeRule:匹配激活路由
    • container:渲染容器
    • 等等…
  2. 启动主应用:调用 start() 方法启动框架。Qiankun 会监听浏览器路由变化,当 URL 匹配到某个子应用的 activeRule 时,就会:
    • 拉取子应用资源(HTML/JS/CSS)
    • 执行子应用的 bootstrap 生命周期做初始化
    • 执行 mount 将子应用挂载到指定容器
  3. 销毁或切换:当路由切走时,会执行子应用的 unmount,将其从页面和内存中卸载,释放相关资源。

生命周期函数(bootstrap / mount / unmount)通常由子应用自己实现,比如在 mount 中做 DOM 渲染,unmount 中解绑事件等。

子应用加载与资源处理

加载策略

Qiankun 在加载子应用时,会先获取子应用的 HTML 入口,然后在解析 HTML 的过程中提取脚本和样式,并动态插入到主应用页面中。这里会对每个 <script><link> 等标签进行处理,以便精确地控制它们的执行顺序和作用域。

动态插入与执行时机

  • Qiankun 会先解析并收集子应用的所有脚本和样式链接,做一些去重和缓存。
  • 当子应用被激活时,才真正执行这些脚本,并将 CSS 样式插入到页面中。结合沙箱机制,脚本里面对全局变量的访问不会影响主应用和其他子应用。

样式隔离

Qiankun 默认不会为每个子应用做严格的 CSS Scope(比如像 Shadow DOM 那样彻底隔离),但通过加前缀或命名规范可以在一定程度上避免样式冲突。此外也有社区提供了更多样式隔离的解决方案,比如动态给 CSS 选择器加上特定前缀,但通常会影响性能或兼容性,需要权衡。

数据通信与共享

当主应用和子应用之间需要共享一些全局数据(比如用户登录信息、权限、公共状态等),Qiankun 提供了三种常用的做法:

  1. props 传递:在注册子应用时,可以通过 props 字段把数据或方法透传给子应用;子应用可直接在生命周期函数的 props 参数中获取到。
  2. 全局数据通信:Qiankun 提供了 initGlobalState 等方法创建全局数据仓库,主应用和各子应用都可订阅数据变化并触发更新,从而共享一份全局可观测数据。
  3. 自定义事件或消息总线:可以通过浏览器原生的 window.postMessage 或者类似 Pub/Sub 的消息总线进行通信,Qiankun 并没有做特别的封装,可以自由发挥。

预加载和性能优化

Qiankun 有一个非常实用的特性:预加载子应用。在主应用空闲时,利用浏览器空闲时间与网络带宽,提前拉取并解析子应用的静态资源(HTML/JS/CSS),这样在真正激活子应用时就只需要执行脚本,无需再次下载,提高了首屏速度。

  • 在调用 start({ prefetch: true }) 时,Qiankun 会在主应用启动后、空闲时,对已注册但尚未激活的子应用进行预下载。
  • 对于大型前端应用场景,这通常可以显著优化用户切换到子应用时的加载体验。

与 single-spa 的关系

Qiankun 最初就是构建在 single-spa 之上,两者的核心差异在于:

  1. 封装程度不同
    • single-spa 本身提供了非常原生的生命周期管理,但对沙箱或样式隔离等没有做太多处理,需要开发者自己去实现。
    • Qiankun 在 single-spa 基础上封装了一系列开箱即用的能力,如 Proxy 沙箱、预加载、全局状态管理等,大幅简化了接入微前端的工作量。
  2. 使用方式
    • single-spa 更像一个微前端内核,需要开发者在配置路由、加载器、沙箱方面编写更多自定义逻辑;
    • Qiankun 则倾向“一站式”,通过简单的 registerMicroAppsstart 等 API,就可以让多个子应用跑起来,而且默认拥有良好的隔离能力。
  3. 社区生态
    • single-spa 在海外社区相对活跃,文档体系成熟;
    • Qiankun 在国内社区使用率高,阿里系和大厂在内部也有大量实践,中文文档/案例丰富。

总结与思考

  • 核心思路:Qiankun 是一个基于 single-spa 升级而来的微前端解决方案,其设计思想集中于:使用沙箱机制实现前端应用的强隔离,并封装掉复杂的资源加载与路由管理细节,给开发者提供相对简单的一键式微前端体验。
  • 沙箱重中之重:微前端的灵魂在于保证不同子应用之间的隔离与独立运行。Qiankun 通过 Proxy 沙箱最大化地减少了全局污染的风险,并在主应用移除子应用时能够释放或还原全局环境。
  • 零配置和易用性:对大部分项目而言,只需提供子应用的入口地址、激活路由和挂载容器,就可以快速让微前端跑起来,降低了改造和迁移成本。
  • 性能优化:预加载特性提升了应用切换时的响应速度,适用于多数 B 端管理系统场景。
  • 局限与挑战
    • 子应用间如果需要复杂的数据通信或共享模型,仍需要开发者自行设计。
    • 样式隔离并不是彻底的 Shadow DOM 级别隔离。
    • 当子应用很多且体积很大时,资源预加载策略和内存占用是需要重点关注和权衡的。
    • 在某些情况下(比如一些第三方库会直接操作全局变量),沙箱无法完全封装所有外部副作用,需要手动处理。

总的来看,Qiankun 是当前国内非常流行且成熟的微前端解决方案,能很好地解决多技术栈并存、团队独立开发部署、渐进升级的需求。它从微前端架构的思想出发,配合完整的沙箱隔离和易用的接口,实现了应用解耦与协作,大幅简化微前端的上手难度。对多数中大型前端项目而言,如果有需求将前端拆分为多个子应用独立部署,Qiankun 都会是一个值得尝试的选择。