概念
微前端是一种类似于微服务架构的前端架构,它将一个大型的单页应用拆分成多个独立开发、独立部署、技术栈无关的“微应用”,最后再组合成一个完整的应用。官网: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 的解析过程)
- fetch HTML: qiankun 根据你配置的
entry地址,通过fetchAPI 获取子应用的 HTML 文件内容。 - DOM 解析: 使用
DOMParserAPI 将获取到的 HTML 文本解析成一个 DOM 对象。 - 资源处理:
- 脚本(Scripts): 提取所有
<script>标签。对于带src的,获取绝对路径并缓存;对于 inline script,直接记录其内容。 - 样式(Stylesheets): 提取所有
<link rel="stylesheet">和<style>标签,同样处理其链接或内容。
- 脚本(Scripts): 提取所有
- 动态加载与执行: 在主应用提供的容器节点内,插入一个用于渲染的 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等全局只读属性)。
- set: 当微应用修改属性(如
- 优势: 多个微应用可以同时运行,彼此的
proxyWindow是隔离的,互不影响。
qiankun 是如何实现样式隔离的?
- Scoped CSS(默认方案):
- 原理: qiankun 会自动为子应用容器包裹一个特殊的 DOM 节点(如
div[data-qiankun=”appName“]),并对子应用的所有动态加载的样式规则添加这个特殊选择器的前缀,从而将样式的影响范围限制在该容器内部。 - 局限性: 对于子应用内手动在
<head>里插入的<style>或<link>标签(如在main.js顶部导入的 CSS),这种方案无法处理,可能会导致样式污染。
- 原理: qiankun 会自动为子应用容器包裹一个特殊的 DOM 节点(如
- Shadow DOM(严格隔离方案):
- 原理: 将子应用的容器直接挂载到一个
ShadowRoot下。Shadow DOM 的特性提供了天然的样式隔离和 DOM 隔离。 - 用法: 在主应用注册微应用时,配置
sandbox: { strictStyleIsolation: true }。 - 优缺点: 隔离性最强,但可能导致某些第三方 UI 库的样式失效(因为样式无法穿透 Shadow DOM 边界),且子应用内弹窗等需要挂载到
body的组件可能会出现问题。
- 原理: 将子应用的容器直接挂载到一个
接入 qiankun,子应用需要做哪些改造?
- 导出生命周期钩子: 子应用的入口文件(如
main.js或index.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.dispatchEvent和window.addEventListener进行通信。更松耦合,但需要自己定义事件名和数据结构。 - Props 传递: 在主应用注册子应用时,可以通过
props字段向下传递数据或方法。适用于一次性或固定的数据传递。 - 状态管理库集成: 如果主/子应用技术栈一致(如都是 Redux 或 Vuex),可以考虑共享同一个 Store 实例。但要注意命名空间隔离。
常见问题
- 子应用资源加载 404: 检查
publicPath配置是否正确。 - 路由冲突: 检查主/子应用的路由
base和activeRule配置是否正确,避免相互覆盖。 - 样式污染/丢失: 检查样式隔离配置,确认是否是 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 定位类似,也是一个功能完备的解决方案。