概念微前端主要就是把一个前端的单体应用拆分成多个可以独立开发、独立部署、运行时再被组合起来的小应用每一个应用都有自己的路由、组件、构建产物主应用则是负责把这些东西拼在一个页面之中。类型微前端主流方案可分为路由驱动型、Web Components 组件型、模块联邦型、原生方案四大类其中 qiankun、Module Federation、micro-app、Garfish、Wujie 是最主流的落地选择。为什么要使用微前端独立开发/独立发布一个业务模块发布不拖着整个项目一起发布。多团队协作不同的团队维护不同的子应用边界清晰。渐进式重构老系统不推倒重来可以把新页面逐步迁进来。技术栈共存不同的子应用可以使用不同的前端框架或者不同的版本。核心原理路由/规则驱动的“加载决策”主应用根据当前的URL或者某个规则决定激活哪一个子应用。远程加载子应用资源HTML/JS/CSS并执行主应用拿到子应用的entry去下载子应用资源然后把子应用的代码在当前的页面里面跑起来。生命周期协议mount/unmount主应用必须能够控制子应用什么时候渲染、什么时候卸载因此子应用要暴露一套协议。bootstrap一次性初始化可选。Mount(props)把子应用渲染到主应用给的容器之中。Unmount()彻底卸载清理dom、事件、定时器、全局副作用。核心技术点隔离避免相互污染多个应用跑在同一个页面之中天然就会产生冲突JS全局变量/事件监听谁往Window上挂东西谁监听全局事件。CSS样式污染子应用的样式影响主应用或者其他的子应用。路由冲突多个应用谁都想接管URL。常见的解决方法JS隔离全局变量、事件、定时器等目标让每个子应用看到的全局环境都尽量独立卸载的时候能够把自己的副作用清理干净。沙箱机制使用快照沙箱/代理沙箱加载给子应用前把当前Window做个快照卸载的时候还原。或者给子应用一个代理Window对子应用所有的window.xxx读写都进行拦截读取优先从子应用自己的沙箱拿。写入只写到沙箱不污染真实的Window。优点是对子应用透明不用改太多的代码。运行时劫持清理统一劫持一些全局的api比如window.addEventListener / removeEventListener。setInterval / clearInterval。在子应用的mount阶段记录当前新增的监听器、定时器在unmount阶段统一清理这样在应用卸载后就不会有挂在Window上的监听、定时器残留。模块作用域隔离要求子应用尽量少用window.xxx而是通过模块导入、导出或者依赖注入拿能力。比如把全局事件总线、HTTP客户端做成注入式而不是Window.bus。总结JS隔离就是给每个子应用一个相对独立的Window视角可控的副作用表卸载的时候能够复原环境。CSS隔离样式互不污染命名约定/BRM/命名空间约定所有样式都带上前缀比如主应用.host-xxx。子应用A.app-a-xxx。子应用B.app-b-xxx。在构建时借助CSS Modules、CSS-in-JSstyled-components, emotion, vanilla-extract 等等自动生成局部唯一的类名。选择器作用域化加根节点前缀约定每个子应用在dom里保一层根节点例如div idsubapp-a-root ...子应用所有 DOM... /div。构建或者编写样式的时候把所有选择器都自动加上前缀.btn → #subapp-a-root .btn。这样子应用的样式就只会影响自己的那一块dom树了。Shadow DOM/Web Components把子应用封装成Web Component放进 Shadow DOM 中。Shadow DOM 有天然的样式隔离里面的样式不会影响外面外面的也不会影响里面。缺点接入成本高老项目改造较重。样式沙箱运行时动态隔离像 qiankun 的 experimentalStyleIsolation 之类会在运行时对style、link做一些“作用域包装”。动态给选择器加前缀。或者用支持 scoped 的技术栈做封装。总结CSS隔离就是让每一个子应用的样式都只作用在自己的dom子树上。路由隔离避免抢路由目标多个子应用都有自己的路由系统不要互相抢URL。URL前缀划分主应用控制“一级路由”约定每个子应用一个前缀/app-a/** → 子应用 A。/app-b/** → 子应用 B。子应用只在自己的前缀范围内使用路由如 React Router basename/app-a。主应用统一接管历史记录History主应用负责监听 popstate / hashchange根据规则决定什么时候激活哪个子应用。如何把路由事件转发给当前激活的子应用。子应用内部只操作相对路由不直接乱改 window.location。hash 路由与 history 路由分层一层用 path一层用 hash主应用用 path/micro-app#...。子应用内部用 hash 路由。缺点URL 观感不好但简单粗暴。通信主子/子子之间如何传递数据微前端拆开后应用之间仍需要登录态token/用户信息。主题、语言。业务事件比如“购物车数量变化”。常见手段主应用下发 props最简单直接。全局状态/事件总线qiankun 有 initGlobalState。URL/Storage简单但耦合强。常见的解决方案主应用下发 props最简单方式主应用在 registerMicroApps 或 mount 时把一些信息放在 props 里比如userInfo, token, theme, onLogout 函数等。子应用在生命周期的 mount(props) 里接收这些东西挂到自己的状态或上下文里使用。特点简单直观、强类型友好TS 容易约束。适合“一对多”的自上而下配置比如主题、语言、当前用户。全局状态 / 事件总线qiankun 的 initGlobalState方式主应用创建一个“全局状态容器”任何子应用都可以订阅/修改。比如qiankun 提供了一套 initGlobalState API主应用初始化全局 state并把修改/订阅方法通过 props 下发给子应用。子应用调用 setGlobalState / onGlobalStateChange 做跨应用通信。或者使用 事件总线在主应用创建一个全局 EventBus可用 mitt / Node 风格 EventEmitter。主/子应用通过订阅/触发特定事件来进行通信bus.emit(cart:update, {...})。bus.on(user:logout, handler)。特点适合多子应用之间横向通信子子通信。需要约定事件名/数据结构避免变成“全局垃圾场”。URL / Storage / Cookie环境型通信方式URL把部分关键信息编码到 URL 上比如?langzhthemedark。所有应用只要 respect 这些参数就能“共享”上下文。localStorage / sessionStorage / cookie登录态 token、最近选择的语言、主题等放在浏览器存储中。各子应用从同一 key 读取。特点简单无需额外协议适合一些“环境类/配置类”的共享信息。缺点是耦合强、类型弱、生命周期不好控写了就到处可读。综合实践建议隔离优先顺序CSS先通过“根节点前缀 CSS Modules/CSS-in-JS”解决 80% 问题。JS尽量少用 window 直挂对副作用集中管理封装一层 app-shell。路由主应用定协议子应用遵守前缀/basename 即可。通信优先顺序配置信息用“主应用下发 props”。业务事件用“事件总线 / 全局状态容器”。环境类主题/语言可以同时写到 URL / Storage让页面刷新也能恢复。总结微前端不是新框架而是一套运行时的组合机制主应用决定何时加载哪个子应用拉取并执行它的资源通过mount/unmount控制渲染同时用隔离与通信保证多个应用能够在同一页面共存。主流方案分类路由驱动类型概念/原理把“子应用”当成“页面级应用”由基座根据 URL/路由规则决定加载哪子应用并把它挂载到容器里子应用通常仍是独立构建产物HTML/JS/CSS。实现思路路由命中 → 拉取子应用资源entry/manifest→ 运行子应用生命周期bootstrap/mount/unmount→ 卸载时清理 DOM/事件/定时器/样式影响。核心技术点①应用注册与路由匹配prefix/path-to-regexp、hash/history 兼容、子应用间跳转规范。②加载与运行时动态插入script/link、HTML entry 解析、资源重写、预加载、沙箱执行上下文。③隔离与卸载JS 隔离Proxy 沙箱、快照沙箱window 变更回滚CSS 隔离scoped、CSS Module、运行时样式前缀/Shadow 方案的“半隔离”彻底卸载DOM 清空、全局监听清理、全局状态回滚、定时器清理④通信全局事件总线、基座下发 props、共享 store、URL/自定义协议。⑤共享依赖通常较弱更多是“各自打包”可配合 externals 或 CDN 公共库。优势(1) 上手与落地快对现有 SPA 侵入相对可控适合“巨石拆分”。(2) 团队自治强子应用可独立发布、独立技术栈到一定程度。(3) 体验可做到不错预加载、按需加载、路由级拆分自然。缺点/特点(1) 隔离不彻底尤其 CSS/全局副作用要靠工程规范 沙箱补丁。(2) 共享依赖与性能易出现重复依赖、首屏/切换时多次加载优化成本高。跨应用 UI 组合能力弱更像“页面切换”不是“组件级拼装”。qiankun阿里基于 single-spa。以一个基于vite创建的react项目react子应用 vue子应用的微前端demo安装qiankun命令npm install qiankun核心概念核心概念 主应用负责注册和管理子应用提供导航和容器子应用独立开发的小型应用通过 qiankun 集成到主应用中生命周期函数 bootstrap 、 mount 、 unmount 用于子应用的初始化、挂载和卸载激活规则通过路由路径匹配决定何时加载哪个子应用技术栈 主应用React Vite qiankunVue 子应用Vue Vite vite-plugin-qiankunReact 子应用React Vite vite-plugin-qiankun打开 react-subapp/vite.config.js 文件添加 qiankun 配置import { StrictMode } from react import { createRoot } from react-dom/client import ./index.css import App from ./App.tsx import { registerMicroApps, start } from qiankun // 注册子应用 registerMicroApps([ { name: vue-subapp, // 子应用名称随便起只要唯一 entry: //localhost:3001, // 子应用的访问地址 container: #subapp-container, // 子应用要渲染到主应用的哪个容器里 activeRule: /vue-subapp, // 访问什么路径时加载该子应用 }, { name: react-subapp, entry: //localhost:3002, container: #subapp-container, activeRule: /react-subapp, }, ]) // 启动 qiankun start() createRoot(document.getElementById(root)!).render( StrictMode App / /StrictMode, )代码解释registerMicroApps 注册子应用告诉 qiankun 有哪些子应用以及它们的配置start 启动 qiankun开始监听路由变化自动加载/卸载子应用修改 App.tsx 文件打开 src/App.tsx 文件添加导航链接和子应用容器import { useState } from react import reactLogo from ./assets/react.svg import viteLogo from ./assets/vite.svg import heroImg from ./assets/hero.png import ./App.css function App() { const [count, setCount] useState(0) return ( {/* 导航链接 */} nav classNamenav a href/主应用/a a href/vue-subappVue 子应用/a a href/react-subappReact 子应用/a /nav section idcenter div classNamehero img src{heroImg} classNamebase width170 height179 alt / img src{reactLogo} classNameframework altReact logo / img src{viteLogo} classNamevite altVite logo / /div div h1Get started/h1 p Edit codesrc/App.tsx/code and save to test codeHMR/code /p /div button classNamecounter onClick{() setCount((count) count 1)} Count is {count} /button /section {/* 子应用容器 */} div idsubapp-container classNamesubapp-container/div {/* 其他内容... */} / ) } export default App代码解释nav classNamenav 导航栏用于在主应用和子应用之间切换div idsubapp-container 子应用容器子应用会渲染到这个容器里添加样式App.css打开 src/App.css 文件添加导航栏和子应用容器的样式/* 导航栏样式 */ .nav { display: flex; /* 横向排列 */ gap: 20px; /* 链接之间的间距 */ padding: 20px; /* 内边距 */ background: var(--accent-bg); /* 背景色 */ border-bottom: 1px solid var(--border); /* 底部边框 */ position: fixed; /* 固定在顶部 */ top: 0; /* 顶部距离为0 */ left: 0; /* 左侧距离为0 */ right: 0; /* 右侧距离为0 */ z-index: 1000; /* 层级确保在最上层 */ width: 100%; /* 宽度100% */ box-sizing: border-box; /* 盒模型确保内边距不影响宽度 */ margin: 0; /* 外边距为0 */ /* 链接样式 */ a { color: var(--accent); /* 文字颜色 */ text-decoration: none; /* 去掉下划线 */ font-size: 16px; /* 字体大小 */ padding: 8px 12px; /* 内边距 */ border-radius: 4px; /* 圆角 */ transition: background-color 0.3s; /* 背景色过渡效果 */ /* 鼠标悬停时的样式 */ :hover { background: var(--social-bg); /* 悬停时的背景色 */ } } } /* 为内容区域添加顶部边距避免被固定导航遮挡 */ #center { margin-top: 80px; /* 顶部边距与导航栏高度匹配 */ } /* 子应用容器样式 */ .subapp-container { width: 100%; /* 宽度100% */ min-height: 400px; /* 最小高度确保子应用有足够空间 */ padding: 20px; /* 内边距 */ border: 1px solid var(--border); /* 边框 */ border-radius: 8px; /* 圆角 */ margin: 20px 0; /* 上下外边距 */ background: var(--bg); /* 背景色 */ }代码解释 position: fixed 固定导航栏在顶部确保无论滚动到哪里都能看到z-index: 1000 确保导航栏在子应用之上不会被子应用遮挡margin-top: 80px 为内容区域添加顶部边距避免被导航栏遮挡Vue子应用安装npm install vite-plugin-qiankun打开 vue-subapp/vite.config.js 文件添加 qiankun 配置import { defineConfig } from vite import vue from vitejs/plugin-vue import qiankun from vite-plugin-qiankun // https://vite.dev/config/ export default defineConfig({ plugins: [ vue(), qiankun(vue-subapp, { useDevMode: true, // 开发模式方便调试 }), ], server: { port: 3001, // 子应用端口与主应用中配置的一致 cors: true, // 允许跨域主应用需要访问子应用 }, })代码解释 cors: true 允许跨域请求因为主应用和子应用运行在不同端口port: 3001 设置子应用的端口与主应用中 entry 配置的端口一致qiankun(vue-subapp, { useDevMode: true }) 配置 qiankun 插件指定子应用名称为 vue-subapp开启开发模式打开 vue-subapp/src/main.js 文件添加 qiankun 生命周期函数import { createApp } from vue import ./style.css import App from ./App.vue let app null // qiankun 生命周期函数 - 初始化 export async function bootstrap() { console.log(Vue 子应用 bootstrap) } // qiankun 生命周期函数 - 挂载 export async function mount(props) { console.log(Vue 子应用 mount, props) // 如果有容器主应用传入就渲染到容器里否则渲染到 #app独立运行时 app createApp(App) app.mount(props.container ? props.container.querySelector(#app) : #app) } // qiankun 生命周期函数 - 卸载 export async function unmount() { console.log(Vue 子应用 unmount) // 卸载应用清理资源 app.unmount() app null } // 独立运行时不在 qiankun 环境中 if (!window.__POWERED_BY_QIANKUN__) { createApp(App).mount(#app) }代码解释window.__POWERED_BY_QIANKUN__ qiankun 注入的全局变量用于判断是否在 qiankun 环境中运行unmount 子应用卸载时调用清理资源mount 子应用挂载时调用将应用渲染到主应用的容器中bootstrap 子应用初始化时调用一般用于初始化一些资源React子应用安装npm install vite-plugin-qiankun打开 react-subapp/vite.config.js 文件添加 qiankun 配置import { defineConfig } from vite import react from vitejs/plugin-react import qiankun from vite-plugin-qiankun // https://vite.dev/config/ export default defineConfig({ plugins: [ react(), qiankun(react-subapp, { useDevMode: true, }), ], server: { port: 3002, // 子应用端口与主应用中配置的一致 cors: true, // 允许跨域 }, })打开 react-subapp/src/main.jsx 文件添加 qiankun 生命周期函数import { StrictMode } from react import { createRoot } from react-dom/client import ./index.css import App from ./App.jsx let root null // qiankun 生命周期函数 - 初始化 export async function bootstrap() { console.log(React 子应用 bootstrap) } // qiankun 生命周期函数 - 挂载 export async function mount(props) { console.log(React 子应用 mount, props) // 如果有容器主应用传入就渲染到容器里否则渲染到 #root独立运行时 const rootElement props.container ? props.container.querySelector(#root) : document.getElementById(root) root createRoot(rootElement) root.render( StrictMode App / /StrictMode ) } // qiankun 生命周期函数 - 卸载 export async function unmount() { console.log(React 子应用 unmount) // 卸载应用清理资源 root.unmount() root null } // 独立运行时不在 qiankun 环境中 if (!window.__POWERED_BY_QIANKUN__) { const rootElement document.getElementById(root) root createRoot(rootElement) root.render( StrictMode App / /StrictMode ) }常见的问题和解决方法端口冲突启动应用时提示端口被占用 解决方案 修改子应用的 port 配置确保每个应用使用不同的端口跨域错误主应用无法加载子应用控制台提示跨域错误 解决方案 在子应用的 vite.config.js 中设置 cors: true子应用不显示点击子应用链接后子应用不显示 解决方案 检查主应用的 activeRule 配置是否与子应用的路由匹配。检查子应用的 entry 配置是否正确。检查子应用是否正常启动访问子应用的独立地址如 http://localhost:3001/ 导航链接不显示点击子应用后导航链接消失 解决方案 确保导航栏使用 position: fixed 固定在顶部。设置足够高的 z-index 确保导航栏在子应用之上。single-spa微前端 “鼻祖”。Web components组件类型micro-app京东。Wujie腾讯。Garfish字节跳动。模块联邦类型Webpack 5 Module FederationMF。原生方案Iframe。动态 Script 手动管理。选择建议企业级、多技术栈、渐进迁移→ 选 qiankun最稳妥。同技术栈、大量复用、Webpack 5→ 选 Module Federation性能最优。追求轻量、组件化、Vite 友好→ 选 micro-app。首屏 / 性能 / 安全敏感、ToB→ 选 Wujie。字节技术栈、需要强监控→ 选 Garfish。第三方嵌入、快速验证→ 选 Iframe。