← 全部材料 微前端
MICRO-FRONTEND · 10 个高频考点

先证明需要,再谈方案

微前端是少数选错代价远大于选对收益的架构。这份把四条路线、三种沙箱、样式隔离的四代方案摊开讲,每一节都给出"什么时候不该用"。

01先问组织几个团队、发布节奏是否真的冲突。答不上来就先别上。
02认准隔离强度iframe 最强体验最差,MF 最弱但能共享代码,中间是 qiankun 和 wujie。
03能说清逃逸点Proxy 沙箱拦不住什么、样式隔离治不了什么,这才是深度。
1

微前端解决的不是技术问题,是组织问题★

先说清代价再谈方案
微前端真正要解决的是多个团队要独立发布同一个前端这件事。如果只有一个团队、一个发布节奏,引入微前端只会凭空多出三层复杂度:沙箱、隔离、通信。所以面试里第一句应该是"先确认是不是真的需要"。

没有微前端时,多团队的典型冲突

  • 两个团队共用一个仓库,发布要互相等
  • 升级一个公共依赖(比如 React 大版本)要全量回归
  • 一个团队的代码风格和目录结构扩散到全局
  • 构建时间随项目线性增长,改一行要等十分钟

微前端换来的能力

  • 每个子应用独立仓库、独立构建、独立发布
  • 技术栈可以不同(老系统 Vue2 不用立刻升级)
  • 故障隔离,一个子应用崩了不影响主框架
  • 代价:多了一层运行时,调试和排查变复杂
面试怎么答微前端解决的是多个团队独立发布同一个前端的问题,不是纯技术选型。它的收益是独立仓库独立构建独立发布、技术栈可以不同、故障隔离;代价是多一层运行时,本地调试、依赖复用、样式隔离、错误定位都会变复杂。所以在技术方案之前先确认是不是真的需要,如果痛点只是构建慢和互相等待,monorepo 加 CI 缓存可能是更轻的解法。
2

四条路线:iframe / qiankun / wujie / Module Federation★

隔离强度和体验是反比
四条路线的核心差别是隔离靠什么:iframe 靠浏览器原生、qiankun 靠 Proxy 沙箱、wujie 靠"iframe 跑 JS + shadow DOM 渲染"、Module Federation 干脆不做隔离只管共享。隔离越强,体验和改造成本越差。
四条路线的取舍【iframe】隔离 100%,体验 60% JS / CSS / 全局变量全隔离,靠浏览器原生 问题:弹窗无法覆盖父页面、路由和前进后退割裂、 父子通信只能 postMessage、首屏多一次文档加载 适合:嵌入第三方不可信页面、老系统改造预算极低 【qiankun】隔离 80%,体验 85% 基于 single-spa,JS 用 Proxy 沙箱,CSS 用 scoped 或 shadow DOM 子应用按约定的生命周期导出 bootstrap / mount / unmount 问题:沙箱不是真隔离(有些全局逃逸)、样式隔离靠约定 适合:大多数中大型企业的后台系统 【wujie】隔离 95%,体验 85% 每个子应用一个隐藏 iframe 跑 JS(拿到原生隔离) 渲染到主应用的 shadow DOM 里(布局和弹窗正常) 问题:社区和生态比 qiankun 小 适合:既想要强隔离又不想牺牲体验 【Module Federation】不做隔离,只做共享 webpack 5 / Vite 的运行时模块共享 优点:依赖能共享一份,不重复打包 缺点:要求所有子应用用同一套构建工具和框架版本 适合:同一技术栈、想共享依赖的场景
面试怎么答四条路线的核心差别是隔离靠什么。iframe 靠浏览器原生,隔离最强但体验最差,弹窗盖不住父页面、路由割裂、通信只能 postMessage。qiankun 基于 single-spa,用 Proxy 做 JS 沙箱、scoped 或 shadow DOM 做样式隔离,是大多数后台系统的选择,但沙箱不是真隔离。wujie 把执行和渲染拆开,JS 跑在隐藏 iframe 里拿到原生隔离,DOM 渲染到 shadow DOM 里保证布局正常。Module Federation 不做隔离,只解决运行时依赖共享,要求技术栈统一。
3

沙箱的三种实现★

这是微前端最常被追问的技术点
JS 沙箱要解决一件事:子应用改 window 不影响别人,切走时能还原。三种实现:快照沙箱(记录前后 diff,只能单实例)、Proxy 沙箱(虚拟 window,可多实例)、iframe 沙箱(真 window,最彻底)。
◐ 可交互:开不开沙箱,子应用之间会不会互相污染
每个子应用有自己的假 window,写进去的东西出不来。
子应用 A
A 读到 theme:undefined
子应用 B
B 读到 theme:undefined
真实 window 上多出来的 key
(干净)
沙箱开启时,A 写的东西只在 A 的假 window 上,B 读不到,真实 window 也是干净的。
三种沙箱的对比【快照沙箱】snapshot 激活时给 window 拍快照,退出时 diff 出改了什么、还原 优点:实现简单 缺点:只能有一个子应用在跑;window 属性多时 diff 很慢 用于:qiankun 的单实例模式 【Proxy 沙箱】proxy 每个子应用一个 Proxy 假 window 优点:支持多实例同时运行,不用遍历 window 缺点:逃逸情况多(document.body 直接改、事件监听、定时器) 用于:qiankun 的多实例模式、大部分自研方案 【iframe 沙箱】 子应用在 iframe 里执行,window 是真的、原生隔离 优点:隔离彻底,不用维护 Proxy 的边界 缺点:iframe 和主文档通信有成本 用于:wujie
面试怎么答JS 沙箱要保证子应用改 window 不影响别人、切走能还原。三种实现:快照沙箱在激活时给 window 拍快照,退出时 diff 还原,实现简单但只支持单实例;Proxy 沙箱给每个子应用一个假 window,支持多实例,但 document、定时器、事件监听这些直接操作真对象的会逃逸,要额外代理和清理;iframe 沙箱在真 window 里执行,隔离最彻底。实现 Proxy 沙箱的关键是用 with 把假 window 放进作用域链最前面。
4

样式隔离:从约定到真隔离★

四代方案,越往后越狠
样式隔离的难度在于CSS 是全局的,而子应用的样式表被插入到主文档里。四代方案:命名约定 → 运行时改选择器加前缀 → shadow DOM → iframe。qiankun 默认给的是第二种。
四代方案与它们的破绽【一、命名约定】BEM 加应用前缀 .app-order__title { } 破绽:靠人守,第三方库和 UI 框架的全局样式照样污染 【二、运行时改选择器】qiankun 的 experimentalStyleIsolation .title { } → div[data-qiankun="order"] .title { } 优点:不用改子应用代码 破绽:@keyframes、@font-face、:root 变量不会被改写; 动态插入的 style 标签要监听;!important 仍然穿透 【三、shadow DOM】真隔离 shadowRoot 里的样式出不去,外面的进不来 破绽:弹窗渲染到 body 时会脱离 shadow, 第三方库大量依赖 document 查询会失效 【四、iframe】最彻底 style 完全独立,连 CSS 变量都不继承
面试怎么答样式隔离难在 CSS 是全局的,而子应用的样式表插在主文档里。方案有四代:命名约定靠自觉、运行时给选择器加前缀(qiankun 默认做法,但 @keyframes 和 :root 变量改不到)、shadow DOM 真隔离但弹窗会脱离、iframe 最彻底但体验差。实际项目里更麻烦的往往是 CSS 变量和 UI 库的全局 reset 泄漏,以及子应用弹窗挂到 document.body 后 z-index 打架,这些要靠统一挂载点和 z-index 分段约定解决。
5

路由分发:谁管 URL

主应用统一管,还是子应用自管
两种模式:主应用统一管路由(子应用作为某个路径前缀下的内容,URL 由主应用解析)和子应用自管路由(主应用只负责加载,子应用内部有自己的 router)。前者简单,后者适合子应用本身是个完整应用的情况。
面试怎么答路由分发有两种模式。一是主应用统一管,用 activeRule 按路径前缀决定激活哪个子应用,子应用作为该前缀下的内容,这种方式简单,子应用的 router 要设 base 前缀。二是子应用自管路由,主应用只管加载和传 base。不管哪种,服务端都要把子应用路径回退到主应用入口,且 Nginx 的前缀配置要和 activeRule 对齐,否则刷新会 404。
6

公共依赖:共享还是各带一份★

体积膨胀的主要来源
默认情况下每个子应用都会打包一份 React / Vue,主应用再带一份,就是三份。解法有三:externals 外置由主应用提供(简单但版本必须一致)、Module Federation 运行时共享(灵活)、不管(子应用按需懒加载时影响有限)。
三种共享方式的取舍【externals】主应用提供,子应用不打包 // webpack externals: { react: 'React', 'react-dom': 'ReactDOM' } // 子应用里 import React from 'react' 会被替换成 window.React 优点:立竿见影,体积直接减掉 缺点:版本强绑,子应用不能用不同的 React 版本 【Module Federation】运行时按需共享 shared: { react: { singleton: true, requiredVersion: '^18' } } 优点:可以指定版本范围,singleton 保证全局一份 缺点:要求都用 MF 体系构建 【各带一份】 优点:子应用完全自治,升级互不影响 代价:体积翻倍(React + ReactDOM 约 130KB gzip) 什么时候可接受:子应用是懒加载的,首屏不受影响
面试怎么答默认每个子应用都打包一份框架,主应用再带一份,体积就翻倍。三种解法:externals 把 React 交给主应用提供,最简单但版本强绑;Module Federation 做运行时共享,可以指定版本范围,singleton 保证全局一份;或者干脆各带一份,代价是体积翻倍,但子应用懒加载时首屏不受影响。要注意 React 这类必须 singleton,两份会导致 Invalid hook call 错误。
7

通信:别让子应用互相知道

中心化的消息总线
原则是主应用做总线,子应用之间不直接通信。做法是主应用提供一个事件总线(甚至是状态 store)通过 props 注入,子应用只发事件不关心谁听。这样加一个新子应用不用改任何已有应用。
面试怎么答通信的原则是主应用做总线,子应用之间不直接通信,这样新增子应用不用改已有的。实现上是主应用提供一个事件总线或共享 store,通过 props 注入给子应用。事件适合"发生了某件事",共享状态适合"某个值变了",后者更好,因为后加载的子应用能直接读到当前值,不会错过。要注意子应用卸载时必须注销监听,否则重复注册会导致一次事件触发多次。
8

生命周期:那三个函数到底做什么★

bootstrap 只跑一次,这条最要命
single-spa 规定子应用导出三个函数:bootstrap(只执行一次)、mount(每次进入)、unmount(每次离开)。关键差别是bootstrap 不会重复执行,所以全局初始化必须放 mount,不能放 bootstrap——这是最常见的"第二次进入就白屏"的原因。
面试怎么答子应用要导出 bootstrap、mount、unmount 三个函数,其中 bootstrap 整个生命周期只执行一次,mount 和 unmount 每次进出都会执行。所以全局初始化和事件绑定要放 mount 而不是 bootstrap,否则第二次进入就不会执行,表现为白屏。unmount 里必须清理定时器和注销事件监听,否则会泄漏和重复触发。老应用改造可以用 single-spa 提供的框架适配器包一层。
9

什么时候不该上微前端★

三个明确的"不要"
三种情况应该明确拒绝:只有一两个团队、需要频繁共享组件、以及性能是硬指标。微前端的成本是持续的运维成本,不是一次性的改造成本。
先评估这三个问题1. 有几个团队?发布节奏真的冲突吗? 一个团队 → 不需要。用 monorepo 解决构建和复用问题 两个团队但节奏一致 → 大概率也不需要 2. 公共组件多不多? 多 → 微前端会让每个应用各维护一份,反而更痛 替代:内部组件库 + 版本管理 3. 首屏性能是硬指标吗? 是 → 微前端会多一层运行时和重复依赖 替代:单个应用做路由级分包 仍然需要的情况: 多个团队、独立发布是刚需 技术栈暂时无法统一(收购来的系统、老系统) 单仓构建时间已经影响研发效率
面试怎么答三种情况不该上微前端:只有一个团队、公共组件很多、首屏性能是硬指标。因为微前端带来的是持续的运维成本,沙箱、注册、通信、依赖重复都是长期负担。如果动机只是构建慢和想复用,monorepo 加 pnpm workspace、路由级拆包、组件库版本管理这几样更轻。真正需要的是多团队独立发布、技术栈暂时无法统一、单仓构建已成瓶颈这几种情况。
10

Module Federation 到底共享了什么

运行时去别人的产物里拿模块
Module Federation 的核心是运行时:A 应用不需要在构建时知道 B 的存在,而是在运行到某个时刻去 B 的产物里取模块。这让"独立部署 + 共享代码"第一次能同时成立,代价是运行时依赖网络和版本协商。
面试怎么答Module Federation 的核心是运行时共享:应用启动时去 remote 的 remoteEntry 清单里取模块,构建时不需要知道对方存在。这让独立部署和代码共享能同时成立。代价是运行时依赖网络,版本协商也在运行时做,如果某个依赖要求 singleton 而版本对不上,就会出现两份实例。它解决的是模块复用,不解决沙箱和隔离,所以它和 qiankun 这类方案是互补而不是替代关系。
11

速答清单

考前十分钟只看这一页
01微前端真正解决什么问题?
多个团队独立发布同一个前端。不是技术选型,是组织问题。只有一个团队时引入只会多出沙箱、隔离、通信三层复杂度。
02iframe 做微前端最大的问题?
体验:弹窗盖不住父页面、路由历史独立、fixed 定位相对于 iframe、父子只能 postMessage。这是浏览器安全模型的限制,改不了。
03qiankun 的 JS 沙箱怎么实现?
Proxy 给每个子应用一个假 window,用 with 把假 window 放进作用域链最前面,读写都落在假 window 上。
04Proxy 沙箱有哪些逃逸点?
document.body 直接操作、事件监听、定时器、localStorage 这些直接操作真对象的拦不住,要额外代理并在卸载时统一清理。
05快照沙箱和 Proxy 沙箱的区别?
快照在激活时给 window 拍照、退出时 diff 还原,实现简单但只支持单实例;Proxy 用假 window,支持多实例同时运行。
06wujie 的思路是什么?
把执行和渲染拆开:JS 在隐藏 iframe 里跑拿到原生隔离,DOM 渲染到主文档的 shadow DOM 里保证布局和弹窗正常。
07样式隔离有哪几代方案?
命名约定、运行时给选择器加前缀、shadow DOM、iframe。qiankun 默认是加前缀,治不了 @keyframes、:root 变量和 !important。
08子应用第二次进入白屏,通常是什么原因?
把渲染逻辑写在了 bootstrap 里。bootstrap 只执行一次,unmount 清掉 DOM 后不会再跑,所以什么都渲染不出来。
09unmount 里必须做什么?
卸载框架实例、清理定时器、注销事件总线监听、移除全局事件绑定。不做就会泄漏,且事件会重复触发。
10公共依赖怎么共享?
externals 交给主应用提供最简单但版本强绑;Module Federation 运行时共享可指定版本范围;或者各带一份,代价是体积翻倍。
11为什么 React 必须 singleton?
两份 React 各自维护自己的 hooks dispatcher,会导致 Invalid hook call 错误。
12子应用之间怎么通信?
主应用做总线,通过 props 注入。子应用只发事件不关心谁听。共享状态优于事件,因为后加载的应用能直接读到当前值。
13Module Federation 和微前端的关系?
MF 解决运行时模块共享,不解决沙箱和隔离。两者互补:qiankun 管隔离,MF 管共享依赖。
14什么情况不该上微前端?
只有一个团队、公共组件很多、首屏性能是硬指标。这三种情况用 monorepo、路由拆包、组件库版本管理更轻。
15微前端后端的路由怎么配?
所有子应用路径都回退到主应用入口,主应用按 activeRule 判断加载哪个子应用。Nginx 的前缀配置要和 activeRule 对齐。