公司动态

微前端实践:从概念、原理到 qiankun 落地

📅 2026/7/18 3:46:51
微前端实践:从概念、原理到 qiankun 落地
目录一、什么是微前端1.1 概念1.2 解决什么问题1.3 微前端 vs 微服务二、微前端的基本原理2.1 四个基本命题2.2 集成时机构建时 vs 运行时2.3 常见实现流派2.4 四个命题的通用解法三、qiankun 原理2.1 整体定位基于 single-spa 的封装2.2 路由劫持2.3 HTML Entry核心加载机制2.4 JS 沙箱隔离的核心2.5 CSS 隔离2.6 生命周期四、实践案例三技术栈混合微前端3.1 整体架构3.2 基座实现3.3 子应用接入的通用改造3.4 各技术栈的差异五、踩坑记录坑 1You need to export lifecycle functions坑 2ReactDOM.render is not a function坑 3Failed to fetch坑 4Angular 版本与构建器兼容坑 5zone.js 隔离问题六、总结参考本文从「为什么需要微前端」讲起梳理 qiankun 的核心原理并结合一个真实项目——基座 React Vue Angular 三技术栈混合的完整实践包括踩过的坑与解决方案。一、什么是微前端1.1 概念微前端Micro Frontends是把「微服务」的理念延伸到前端的一种架构风格将一个庞大的前端应用拆分成多个可以独立开发、独立部署、独立运行的小应用再由一个「基座」主应用把它们组合成一个整体呈现给用户。用户看到的是一个完整的网站背后其实是多个独立的前端应用在协同工作。1.2 解决什么问题传统单体前端一个巨大的 SPA随着业务膨胀会遇到技术栈锁定整个项目绑死在某个框架/版本想升级或换技术栈牵一发动全身协作困难几十人改同一个仓库冲突多、构建慢、发布互相阻塞历史包袱老系统想接入新功能只能用老技术栈重写构建效率低改一行代码整个应用重新构建微前端的价值能力说明技术栈无关React、Vue、Angular 可以共存老系统也能接入独立开发各团队负责各自子应用互不干扰独立部署子应用单独上线不需要整体发布增量升级可以逐步替换老系统而非推倒重来1.3 微前端 vs 微服务两者理念相通但治理侧重点不同微服务治理面向运行时的分布式系统重点是网络通信可靠性服务多、调用链长、故障传播。微前端治理面向浏览器里的集成运行重点是隔离与协调所有子应用跑在同一个浏览器 window 里反而更容易互相干扰。微前端不需要微服务那套重型治理注册中心、熔断、链路追踪但有自己的命题隔离、通信、共享依赖、统一监控。二、微前端的基本原理在讲具体框架之前先理解微前端不依赖任何框架的通用原理不管用什么方案都要回答同样的四个问题并在「集成时机」和「实现流派」上做选择。2.1 四个基本命题任何微前端方案本质上都要解决这四件事命题要解决什么应用加载如何把子应用的 HTML/JS/CSS 资源拿进来并运行路由调度如何根据 URL 决定当前显示哪个子应用应用隔离多个应用共存时JS 全局变量、CSS 样式如何互不干扰应用通信基座与子应用、子应用之间如何传递数据不同框架的差别就在于用不同手段回答这四个问题。2.2 集成时机构建时 vs 运行时构建时集成子应用作为 npm 包 / 模块被打包进主应用。优点共享依赖方便、类型友好缺点耦合紧任一子应用更新都要重新构建发布主应用失去「独立部署」运行时集成主流子应用独立部署主应用在浏览器运行时动态加载它们。优点真正独立开发、独立部署缺点需要解决运行时的隔离与加载问题single-spa、qiankun 都属于这一类2.3 常见实现流派方案原理优点缺点iframe每个子应用装进一个 iframe天然隔离JS/CSS 完全独立通信麻烦、路由/历史割裂、弹窗被困在 iframe 内、体验差Web Components用浏览器原生自定义元素封装子应用标准化、原生隔离Shadow DOM框架适配成本高、生态不成熟路由分发 JS 加载single-spa主应用监听路由动态加载子应用 JS 并调用其生命周期灵活、体验好隔离要自己处理接入成本高HTML Entry 运行时加载qiankun在 single-spa 基础上用 HTML 作入口 自动 JS/CSS 隔离接入成本低、隔离开箱即用需跨域 fetch依赖沙箱Module Federationwebpack 5构建/运行时共享模块子应用暴露组件被远程加载依赖共享强、粒度灵活偏「模块共享」需统一构建工具服务端组合SSI / 边缘拼装在服务端或 CDN 边缘把多个 HTML 片段拼成一个页面首屏快、SEO 友好前端交互隔离弱架构偏后端2.4 四个命题的通用解法应用加载JS Entry给 JS 文件/ HTML Entry给 HTML/ Module Federation 的 remoteEntry / iframe 的 src路由调度主应用统一劫持路由history/hashchange按规则激活对应子应用子应用用basename加前缀避免冲突应用隔离JSiframe 天然隔离 / Proxy 沙箱 / 快照沙箱CSSiframe 天然隔离 / Shadow DOM / scoped 前缀 / BEM 命名约定应用通信props下发 / 全局状态发布订阅/ 自定义事件EventBus/ URL 参数小结微前端的基本原理就是「运行时把多个独立应用加载到一个页面并解决路由、隔离、通信」。下面要讲的 qiankun正是「运行时集成 HTML Entry Proxy 沙箱」这条流派的成熟实现。三、qiankun 原理qiankun 是蚂蚁金服开源的微前端框架是目前国内最主流的方案之一。它的原理可以拆成几块。2.1 整体定位基于 single-spa 的封装qiankun 底层依赖single-spa。single-spa 只解决「应用生命周期调度」——什么时候加载、挂载、卸载哪个应用但需要手动处理子应用加载与 JS/CSS 隔离。qiankun 在其之上补齐了三块能力single-spa → 生命周期调度激活/卸载哪个应用 qiankun → HTML加载 JS沙箱 CSS隔离 通信2.2 路由劫持start()之后qiankun 劫持浏览器路由变化监听hashchange、popstate重写pushState/replaceState。每次 URL 变化时用当前路径匹配每个子应用的activeRule匹配到就「加载→挂载」离开时「卸载」。2.3 HTML Entry核心加载机制这是 qiankun 相比 single-spa 最大的改进——用子应用的HTML 地址作为入口而不是 JS 文件。entry://localhost:3000// 给整个 HTML而非 app.jsqiankun 通过import-html-entry库fetch这个 HTML然后解析出所有script、link、style把去掉 script 的 HTML 模板作为子应用 DOM 插入基座容器逐个fetchJS 并用eval执行从执行结果中提取生命周期函数好处是子应用几乎不用改打包配置支持任意 chunk 拆分和按需加载代价是需要跨域 fetch子应用要配 CORS。2.4 JS 沙箱隔离的核心多个子应用共享同一个window容易互相污染覆盖全局变量、遗留定时器、污染原型链。沙箱让每个子应用感觉自己独占一个 windowProxy 沙箱默认用Proxy给每个子应用代理一个「假 window」。写只写到隔离空间不污染真实 window读优先读自己的。快照沙箱老浏览器降级挂载前给 window 拍快照卸载时对比还原。子应用卸载时沙箱自动还原全局变量、清理定时器和事件监听做到「切换时干干净净」。2.5 CSS 隔离experimentalStyleIsolation给子应用样式选择器加属性前缀类似 Vue scopedstrictStyleIsolation把子应用塞进 Shadow DOM物理隔离2.6 生命周期qiankun 通过子应用导出的三个函数控制它生命周期时机作用bootstrap首次加载只一次初始化mount每次激活渲染应用到容器unmount每次离开销毁实例防内存泄漏四、实践案例三技术栈混合微前端下面是一个真实落地的项目一个基座同时加载 React、Vue、Angular 三个子应用。3.1 整体架构┌─────────────────────────────────────────────┐ │ 基座应用 (localhost:9000) │ │ webpack qiankun注册与调度子应用 │ │ ┌─────────┬──────────┬──────────────┐ │ │ ▼ ▼ ▼ │ │ /app-react /app-vue /app-angular │ └───┼─────────┼──────────┼──────────────────────┘ ▼ ▼ ▼ React Vue Angular :3000 :8080 :4200应用技术栈端口路由基座webpack 5 qiankun 2.109000—React 子应用React react-router-dom v5 (CRA)3000/app-reactVue 子应用Vue 2.6 vue-router 3 vuex 3 (Vue CLI)8080/app-vueAngular 子应用Angular custom-webpack (NgModule)4200/app-angular目录结构micro-frontend/ ├── src/# 基座应用源码│ ├── index.js# 注册子应用 start()含 zone.js 全局引入│ └── index.html# 基座页面骨架导航 #container 容器├── webpack.config.js# 基座 webpack 配置HtmlWebpackPlugin 自动注入 JS│ ├── react-app/# React 子应用 (CRA)│ └── src/ │ ├── public-path.js# qiankun 动态 publicPath│ ├── index.js# 生命周期挂到 windowReact 18 createRoot│ ├── App.js# react-router-dom v5 路由│ └── setupProxy.js# 开发服务器 CORS│ ├── vue-app/# Vue 2.x 子应用 (Vue CLI)│ ├── src/ │ │ ├── public-path.js │ │ ├── main.js# export 生命周期render 中重建 router│ │ └── router/index.js# 导出 routes 数组│ └── vue.config.js# umd 打包 CORS 8080 端口│ └── angular-app/# Angular 子应用 (Angular CLI)├── src/ │ ├── public-path.ts │ ├── main.ts# export 生命周期│ ├── index.html# app-root 唯一 id zone.js CDN 兜底│ └── app/# APP_BASE_HREF 设路由 baseselector 加唯一前缀├── custom-webpack.config.js# umd 打包 CORS└── angular.json# 切换到 custom-webpack builder3.2 基座实现基座本身是一个简单的 webpack 应用核心是注册子应用并启动importzone.js;// Angular 子应用依赖必须在 qiankun 之前全局引入import{registerMicroApps,start}fromqiankun;registerMicroApps([{name:reactApp,entry://localhost:3000,container:#container,activeRule:/app-react},{name:vueApp,entry://localhost:8080,container:#container,activeRule:/app-vue},{name:angularApp,entry://localhost:4200,container:#container,activeRule:/app-angular},]);// 关闭 prefetch避免预请求尚未启动的子应用导致 Failed to fetchstart({prefetch:false});3.3 子应用接入的通用改造无论哪个技术栈接入 qiankun 都要做这几件事public-pathqiankun 注入__INJECTED_PUBLIC_PATH_BY_QIANKUN__让子应用资源从自己的域名加载导出生命周期bootstrap/mount/unmount路由 baseqiankun 加载时用/app-xxx作为路由前缀独立运行时用/DOM 隔离限制根节点查找范围避免与其他应用冲突CORS开发服务器设置Access-Control-Allow-Origin: *3.4 各技术栈的差异项ReactVueAngular生命周期导出挂到windowCRA 无法改打包格式export UMD 打包export UMD 打包打包格式CRA 默认vue.config.js设 UMDcustom-webpack设 UMD路由 baseBrowserRouter basenameVueRouter baseAPP_BASE_HREFDOM 隔离限定#root限定#appapp-root加唯一 id五、踩坑记录实践中遇到的报错恰好印证了 qiankun 的原理。坑 1You need to export lifecycle functions现象点击子应用报错找不到生命周期函数。原因qiankun 用eval()执行子应用 JS普通的 ES Moduleexport在 eval 作用域里拿不到。解决ReactCRA 不能改打包格式→ 把生命周期挂到window.reactApp { bootstrap, mount, unmount }Vue / Angular → 打包成UMDlibraryTarget: umdUMD 会把导出挂到全局坑 2ReactDOM.render is not a function现象React 子应用挂载报错。原因教程用的是 React 16 的ReactDOM.render而 CRA 实际装的是 React 18该 API 已移除。解决改用ReactDOM.createRoot(el).render(...)卸载用root.unmount()。坑 3Failed to fetch现象加载子应用时网络请求失败。原因start()默认prefetch: true会预加载所有注册的子应用 HTML。未启动的 Vue/Angular 被预请求fetch 失败。解决start({ prefetch: false })只在真正进入子应用时才加载。坑 4Angular 版本与构建器兼容现象最新 Angular 默认用 esbuild 的application构建器不支持自定义 webpack而 qiankun 需要 webpack 的 UMD 输出。解决安装匹配版本的angular-builders/custom-webpack把angular.json的 builder 从angular/build:application切回 webpack 的browser构建器再配 UMD。坑 5zone.js 隔离问题现象Angular 依赖的 zone.js 若打包进子应用在沙箱里可能 patch 到「假 window」出问题。解决zone.js 移到基座全局引入且在 import qiankun 之前Angular 子应用独立运行时用index.html的 CDN 脚本带ignore属性被 qiankun 加载时跳过兜底。六、总结微前端解决单体前端的技术栈锁定、协作困难、增量升级问题核心价值是「独立开发、独立部署、技术栈无关」。qiankun 原理single-spa 生命周期调度 HTML Entry 直接加载子应用整站 Proxy 沙箱做 JS 隔离 前缀/Shadow DOM 做 CSS 隔离。实践关键每个子应用做好 public-path、生命周期导出、路由 base、DOM 隔离、CORS 五件事不同技术栈在「生命周期导出方式」和「打包格式」上有差异。治理演进入门用静态注册即可走向生产时按「隔离 → 错误兜底 → 鉴权/监控 → 应用注册中心 → 共享依赖」的优先级逐步补齐。微前端不是银弹——小项目用它反而增加复杂度。但当团队规模、系统体量、技术栈异构达到一定程度时它是让前端架构可持续演进的有效手段。参考qiankun 官方文档https://qiankun.umijs.org/single-spahttps://single-spa.js.org/