qiankun微前端

6 阅读8分钟

概念

微前端是一种类似于微服务架构的前端架构,它将一个大型的单页应用拆分成多个独立开发、独立部署、技术栈无关的“微应用”,最后再组合成一个完整的应用。官网:qiankun.umijs.org/zh

  • 巨石应用解耦: 随着项目迭代,单体应用变得臃肿、难以维护和协作。
  • 技术栈无关: 允许不同团队使用 React、Vue、Angular 甚至 jQuery 等技术栈,便于技术选型、升级或重构。
  • 独立开发与部署: 各个微应用可以由不同团队独立开发、测试和部署,提升开发效率和发布敏捷性。
  • 增量迁移: 可以平滑地将老旧的巨型应用逐步替换成新的技术栈。

与 Single-SPA 的区别

  • qiankun 是一个基于 Single-SPA 的生产可用的微前端框架。它对 Single-SPA 进行了封装和增强,使其更易于上手和使用。

  • Single-SPA 是一个“路由器”,它规定了应用的生命周期(bootstrap, mount, unmount),但不关心应用如何加载和隔离。qiankun 在 Single-SPA 的基础上,提供了:

    • HTML Entry 的方式加载应用(区别于 Single-SPA 的 JS Entry)。

    • 样式隔离JS 沙箱 等开箱即用的运行时隔离能力。

    • 资源预加载、全局状态通信等实用功能。

HTML Entry 和 JS Entry 的区别

  • JS Entry: 直接指定一个 JS 文件作为应用的入口。这个 JS 文件需要自己负责将应用挂载到 DOM 节点上。这种方式对应用的改造侵入性较强,且难以处理外部 CSS 资源。
  • HTML Entry: 指定一个 HTML 文件作为应用的入口。qiankun 会像浏览器一样去 fetch 这个 HTML,解析其中的 CSS、JS 资源链接,然后动态加载和执行它们。
  • HTML Entry优势:
    • 接入简单: 对子应用改造极小,几乎像配置 iframe 一样简单。
    • 资源管理能力强: 能自动处理所有在 HTML 中声明的资源(CSS、JS、Favicon 等)。
    • 更符合常规开发习惯: 我们平时开发 SPA 也是以一个 HTML 文件为起点。

qiankun 是如何实现应用加载的?(HTML Entry 的解析过程)

  1. fetch HTML: qiankun 根据你配置的 entry 地址,通过 fetch API 获取子应用的 HTML 文件内容。
  2. DOM 解析: 使用 DOMParser API 将获取到的 HTML 文本解析成一个 DOM 对象。
  3. 资源处理:
    • 脚本(Scripts): 提取所有 <script> 标签。对于带 src 的,获取绝对路径并缓存;对于 inline script,直接记录其内容。
    • 样式(Stylesheets): 提取所有 <link rel="stylesheet"><style> 标签,同样处理其链接或内容。
  4. 动态加载与执行: 在主应用提供的容器节点内,插入一个用于渲染的 DOM 结构(如 <div id=‘__qiankun_subapp__’>)。然后按顺序加载并执行解析到的 JS 和 CSS 资源。

qiankun 的 JS 沙箱是如何实现的?有哪些隔离方案?

  • SnapshotSandbox(快照沙箱):
    • 适用场景: 不支持 window.Proxy 的旧浏览器。
    • 原理: 在应用加载(bootstrap/mount)时,给当前 window 对象拍一个“快照”(记录所有属性)。当应用卸载(unmount)时,将 window 对象恢复到这个快照状态,并记录卸载前的变化。下次挂载时,再应用这些变化。效率较低,无法同时运行多个实例。
  • ProxySandbox(代理沙箱/多实例沙箱):
    • 适用场景: 现代浏览器,也是默认和主要的沙箱。
    • 原理: 为每个微应用创建一个假的 window 对象(通常称为 proxyWindow),并通过 new Proxy(window, { ... }) 进行代理。
    • 操作拦截:
      • set: 当微应用修改属性(如 window.a = 1),这个属性会被设置在 proxyWindow 上,而不是真实的 window 上。
      • get: 当微应用读取属性时,会先从 proxyWindow 上找,如果找不到,则 fallback 到真实的 window 上(这样可以访问 location, history 等全局只读属性)。
    • 优势: 多个微应用可以同时运行,彼此的 proxyWindow 是隔离的,互不影响。

qiankun 是如何实现样式隔离的?

  • Scoped CSS(默认方案):
    • 原理: qiankun 会自动为子应用容器包裹一个特殊的 DOM 节点(如 div[data-qiankun=”appName“]),并对子应用的所有动态加载的样式规则添加这个特殊选择器的前缀,从而将样式的影响范围限制在该容器内部。
    • 局限性: 对于子应用内手动在 <head> 里插入的 <style><link> 标签(如在 main.js 顶部导入的 CSS),这种方案无法处理,可能会导致样式污染。
  • Shadow DOM(严格隔离方案):
    • 原理: 将子应用的容器直接挂载到一个 ShadowRoot 下。Shadow DOM 的特性提供了天然的样式隔离和 DOM 隔离。
    • 用法: 在主应用注册微应用时,配置 sandbox: { strictStyleIsolation: true }
    • 优缺点: 隔离性最强,但可能导致某些第三方 UI 库的样式失效(因为样式无法穿透 Shadow DOM 边界),且子应用内弹窗等需要挂载到 body 的组件可能会出现问题。

接入 qiankun,子应用需要做哪些改造?

  • 导出生命周期钩子: 子应用的入口文件(如 main.jsindex.js)需要导出 bootstrap, mount, unmount 三个函数(如果使用 umi 等框架,通常有插件自动完成)。
  • 路由基座配置: 子应用的路由 base(如 Vue Router 的 base,React Router 的 basename)需要设置为 window.__POWERED_BY_QIANKUN__ ? ‘/sub-app-route’ : ‘/’,以确保在主应用下路由能正确匹配。
  • 资源路径修正: 确保子应用的静态资源(JS、CSS、图片)使用绝对路径或正确的 publicPath。通常需要在 Webpack 中配置 publicPath: ‘//localhost:7101’(开发环境)或在打包时设置为 publicPath: ‘/’(生产环境,假设子应用部署在根目录下)。
  • 打包格式: 建议将 Webpack 打包成 umd 格式(libraryTarget: ‘umd’),这样导出的生命周期函数可以被 qiankun 正确识别。

微前端应用间如何通信?qiankun 提供了哪些方式?

  • 全局状态(Actions 通信): qiankun 提供了一个 initGlobalState 方法。
    • 主应用初始化全局状态:const actions = initGlobalState(state)
    • 主/子应用通过 actions.onGlobalStateChange 监听状态变化。
    • 通过 actions.setGlobalState 修改状态,触发监听。
    • 优点:qiankun 官方方案,简单易用。缺点:全局状态需要谨慎设计,避免混乱。
  • 自定义事件: 利用浏览器的 window.dispatchEventwindow.addEventListener 进行通信。更松耦合,但需要自己定义事件名和数据结构。
  • Props 传递: 在主应用注册子应用时,可以通过 props 字段向下传递数据或方法。适用于一次性或固定的数据传递。
  • 状态管理库集成: 如果主/子应用技术栈一致(如都是 Redux 或 Vuex),可以考虑共享同一个 Store 实例。但要注意命名空间隔离。

常见问题

  • 子应用资源加载 404: 检查 publicPath 配置是否正确。
  • 路由冲突: 检查主/子应用的路由 baseactiveRule 配置是否正确,避免相互覆盖。
  • 样式污染/丢失: 检查样式隔离配置,确认是否是 Shadow DOM 导致第三方 UI 库样式异常。
  • JS 沙箱失效: 检查是否有代码直接修改了 window 的不可配置属性(如 window.top),可能需要子应用配合修改。
  • 公共依赖处理: 如何处理主/子应用都使用的公共库(如 React, Vue)以避免重复打包。解决方案可以是配置 Webpack 的 externals,或者使用 CDN 引入。
  • 部署,如果是部署在同一台机器的主子目录下,注意两点:1.前端注册子应用的activerule和部署的子目录(或nginx代理的目录)不能一样;2.部署主子目录,nginx配置中加入alias配置优先子目录的应用,避免子目录应用访问时被当作主应用路由而打不开。

qiankun 和 iframe 方案相比,优劣分别是什么?

  • qiankun 优势:
    • 体验好: 应用切换无刷新,路由状态保持,更像一个单页应用。
    • 更融合: 子应用可以共享主应用的顶部导航、侧边栏等公共部分。
    • 通信容易: 提供了完善的通信机制。
  • qiankun 劣势:
    • 技术复杂度高: 需要处理沙箱、隔离、路由等各种问题。
    • 存在一定的侵入性: 子应用需要一定的改造。
  • iframe 优势:
    • 隔离性最强: 天然的沙箱,样式、JS 全局环境完全隔离。
    • 接入成本极低: 几乎零改造。
  • iframe 劣势:
    • 体验差: 刷新丢失状态,路由无法同步,弹窗限制多。
    • 通信困难: 只能通过 postMessage 进行简单通信。
    • 性能问题: 每个 iframe 都是一个独立的进程,资源消耗大。

其他微前端方案

  • Module Federation(Webpack 5): 当下最热门的“去中心化”微前端方案,更侧重于“微模块”的共享和远程加载,与 qiankun 的“应用聚合”思路不同,但可以结合使用。
  • EMP: 另一个基于 Module Federation 的微前端框架,提供了更多开箱即用的功能。
  • 无界: 腾讯开源的微前端框架,利用 iframe 实现沙箱,结合 WebComponent 实现组件式的渲染,性能优秀。
  • Garfish: 字节跳动的微前端框架,与 qiankun 定位类似,也是一个功能完备的解决方案。