Skip to content
On This Page

面试题[微前端]

1. 微前端基础

1.1 什么是微前端

微前端(Micro-frontend)是一种将后端微服务理念扩展到前端的架构模式,其核心思想是“分而治之”——将大型前端应用拆分为多个独立的小型子应用,每个子应用可独立开发、测试、部署,最终在浏览器中集成运行。微前端借鉴了微服务的架构理念,将微服务的概念扩展到了前端。

1.2 为什么需要微前端 / 解决了什么问题

传统单体架构在大规模前端应用中逐渐暴露三大痛点:

  • 协作效率低下:多团队并行开发时,代码冲突、依赖冲突频繁发生,构建部署耗时超过30分钟成为常态。
  • 技术栈绑定:统一技术栈限制了团队创新空间,新框架引入成本高。
  • 部署风险集中:任意模块的代码变更都需全量发布,影响整体稳定性。

微前端架构通过“分而治之”的治理模式,实现独立开发、独立部署、故障隔离和弹性扩展四大核心价值。据2025年行业调研显示,采用微前端架构的企业平均提升40%的研发效率,降低35%的运维成本。

1.3 微前端适合什么场景

适合的场景:

  • 跨团队协作的大型网站(如电商平台、企业管理系统)
  • 需整合遗留系统与新功能的迭代型项目
  • 要求高频发布且需保障系统稳定性的业务平台(如金融科技网站、在线教育平台)
  • 子应用数量超过5个以上的大型项目

不适合的场景:小型、简单的项目,使用微前端是“杀鸡用牛刀”,反而会增加维护成本。

1.4 主流框架对比

主流框架概况

框架开发公司核心技术首次发布
qiankun(乾坤)蚂蚁集团基于Single-SPA + Proxy沙箱2019.08
wujie(无界)腾讯iframe + Proxy沙箱2022.03
micro-app京东WebComponents + Shadow DOM2021.04
single-spa社区驱动路由劫持 + 生命周期2018.01
Module FederationWebpack5构建时模块联邦2020

核心能力对比

能力qiankunwujiemicro-appsingle-spa
沙箱隔离Proxy+快照沙箱iframe原生隔离Shadow DOM / 代理沙箱需手动实现
通信机制props + globalState + 事件props + window通信 + postMessage数据通信 + 事件总线自定义实现
子应用保活
多应用同时激活
上手难度中等简单简单困难
Vite兼容性需插件原生支持原生支持需适配
隔离安全性极高

⚠️ 注意:qiankun在2024年后维护频率明显下降(近6个月commit次数为0),选型时需关注社区活跃度。

各框架优缺点

qiankun(乾坤)

  • 优点:成熟稳定、经过大规模生产验证、沙箱机制完善
  • 缺点:侵入性较强、沙箱有性能开销、社区维护放缓
  • 适用场景:大型企业级应用、混合技术栈迁移、对隔离性要求较高

wujie(无界)

  • 优点:原生隔离、轻量级、低侵入性、支持保活
  • 缺点:iframe性能开销较大、功能相对局限
  • 适用场景:高安全需求场景、嵌入第三方页面、需要子应用保活

micro-app

  • 优点:组件化接入、低成本接入、性能优异
  • 缺点:生态相对较小、部分UI库不兼容Shadow DOM
  • 适用场景:快速集成、跨团队协作、移动端兼容

1.5 在微前端技术选型的时候是如何考虑的

在子应用单独启动的情况下,要获取主应用的cookie信息,通常需要进行跨域访问,并且主应用需要开放相应的跨域访问权限。具体方法如下:

  1. 跨域访问设置:主应用需要配置允许跨域访问的策略,通常是通过设置CORS(跨源资源共享)策略来实现。可以在主应用的服务器端配置Access-Control-Allow-Origin头部,允许子应用的域名进行跨域访问。
  2. 在子应用中发送请求:子应用可以通过发送HTTP请求到主应用的服务器来获取cookie信息。这可以是一个简单的GET请求,或者使用XMLHttpRequest、Fetch API等技术。
  3. 处理响应:主应用服务器收到子应用发送的请求后,如果已经配置了允许跨域访问,会将响应发送给子应用。子应用可以从响应中获取到主应用的cookie信息。
  4. 处理Cookie:在子应用中接收到主应用的响应后,可以通过相应的方式获取cookie信息,例如,使用Document.cookie属性来获取cookie信息。

需要注意的是,获取主应用的cookie信息可能涉及到安全性和隐私问题,需要确保获取cookie的操作是合法且安全的,同时用户需要明确同意共享这些信息。

2. 微前端进阶

2.1 微前端需要解决哪些核心问题

主要需解决四大关键问题:

核心问题具体内容
JS隔离(沙箱)防止子应用间的全局变量污染(不同子应用可能依赖不同版本的Vue/React)
CSS隔离(样式隔离)防止不同子应用的样式相互干扰、覆盖
路由管理子应用路由与主应用路由的协调、嵌套路由支持
跨应用通信主/子应用之间、子应用之间的数据共享与事件传递

JS隔离的原理与实现方案

微前端场景中,不同子应用可能依赖不同版本的Vue/React,甚至会修改window上的全局变量(如Vue、$)。若不做隔离,子应用A的修改会覆盖子应用B的全局变量,导致功能异常。

常见的JS隔离方案有:

① 快照沙箱(SnapshotSandbox)

  • 原理:子应用挂载前给window拍“快照”,卸载时根据快照恢复window状态
  • 实现:挂载前遍历window保存所有属性的初始值;卸载时新增属性直接删除,修改属性还原为初始值
  • 优点:逻辑简单;缺点:需全量遍历window性能较差,同一时间只能运行一个子应用

② Proxy沙箱(ProxySandbox)

  • 原理:为每个子应用创建独立的fakeWindow,通过Proxy代理对window的访问
  • 实现:get拦截从fakeWindow优先读取,set拦截写入fakeWindow而非真实window
  • 优点:真正的环境隔离;缺点:Proxy无法完全代理所有window操作

③ iframe原生隔离

  • 原理:完全独立的浏览器上下文,天然隔离
  • 优点:隔离性最强、安全性高;缺点:性能开销较大,通信复杂

CSS样式隔离的原理与实现方案

由于CSS的全局性,不同子应用之间的样式可能会相互干扰。主流方案包括:

方案原理优缺点
CSS命名空间为子应用的CSS类名添加唯一前缀(如.app-prefix-btn兼容性好;子应用内部仍可能相互影响
Shadow DOM浏览器原生API,将子应用DOM和样式封装在隔离环境中隔离彻底;部分UI库不兼容,样式穿透困难
动态样式表子应用加载时动态添加style标签,卸载时移除灵活可控;切换时可能有闪烁

主子应用如何通信

主要有以下几种通信方式:

方式说明适用场景
Props传递主应用通过props向子应用传递数据简单数据传递
全局状态管理(initGlobalState)qiankun提供的全局状态池,支持跨应用状态共享复杂状态共享、跨子应用数据同步
自定义事件基于window.addEventListener/dispatchEvent松散耦合的事件通知
postMessage / BroadcastChannel利用HTML5通信APIiframe方案下跨窗口通信
共享状态(Redux/Vuex)通过共享的状态管理库实现统一技术栈的项目

路由冲突如何解决

微前端中,主子应用各自维护路由实例。解决方案:

  • 主应用监听history变化,匹配激活规则决定加载哪个子应用
  • 子应用的路由基于基座应用的URL路径前缀进行匹配
  • 采用路径前缀隔离策略:主应用路由/app1/*/app2/*分别对应不同子应用

生命周期管理

需定义标准化的生命周期钩子,主应用统一调度子应用的启动、挂载、卸载:

javascript
const lifecycle = {
  bootstrap: (props) => Promise,  // 初始化
  mount: (props) => Promise,      // 挂载
  unmount: (props) => Promise     // 卸载
}

3. qiankun

3.1 为什么选择 qiankun

3.2 qiankun 的原理

3.3 qiankun 的微应用加载方案

qiankun 的微应用加载方案是其架构的核心,精髓在于通过HTML Entry解决了传统方案的痛点,并基于此实现了一套高效的加载与隔离机制。

它替代了 single-spa 的 JS Entry,允许你直接提供一个子应用的HTML地址作为入口。

javascript
registerMicroApps([
  { 
    name: 'app react', 
    entry: '//localhost:7100', // 直接提供一个HTML地址
    container: '#subapp-container',
    activeRule: '/app-react'
  }
]);

这样做有两个很明显的好处:

  • 接入更自然:直接提供HTML入口,无需关心JS文件打包的哈希名变化,避免主应用配置频繁变更。
  • 体验更完整:保持了子应用原有的优化能力,比如按需加载、CSS独立打包等都能保留。

核心加载器:import-html-entry

要实现HTML Entry,qiankun封装了专门解析HTML的库:import-html-entryimport-html-entry 的核心任务是将一个HTML地址,转化为一个可执行的子应用:

  1. 获取与解析:通过 fetch 获取子应用入口HTML文件内容。
  2. 资源分离:使用正则表达式解析HTML,将内容分解为三部分:
    • template:处理后的HTML结构。
    • scripts:所有内联和外部引用的JavaScript脚本。
    • styles:所有内联和外部引用的CSS样式。
  3. 处理样式:将所有获取到的CSS(无论是内联还是外部链接)转换为<style>标签并内联进template
  4. 准备脚本:通过getExternalScripts获取所有JS文件的文本内容。
  5. 执行脚本:通过execScripts在沙箱环境中按顺序执行JS代码,从而获取子应用导出的bootstrap, mount, unmount等生命周期函数。

微应用加载的具体流程

整个过程在loadApp方法中被串联起来:

  1. 触发:URL的变化匹配到预设规则,触发子应用的加载。
  2. 加载:调用import-html-entry获取资源模板与生命周期函数。
  3. 隔离:创建JS沙箱和CSS隔离环境,防止应用间相互干扰。
  4. 挂载:将处理后的template挂载到指定的DOM容器中,依次调用bootstrapmount生命周期函数。
  5. 缓存:资源会被缓存,再次加载时速度将大幅提升。

预加载与性能优化

为了优化切换体验,qiankun 提供了智能的预加载机制。默认策略是懒加载,即只在用户首次访问时加载对应子应用资源。

你也可以在主应用启动时,通过start方法的prefetch属性开启预加载:

javascript
start({
  prefetch: true, // 默认行为:在第一个子应用加载完成后,利用空闲时间预加载其他应用[reference:18]
  // prefetch: 'all', // 启动后立即预加载所有应用[reference:19]
  // prefetch: ['app1', 'app2'], // 只预加载指定的应用[reference:20]
});

沙箱方面,qiankun 提供了三代演进方案:

  • 快照沙箱(SnapshotSandbox):最早版本,性能较差,仅用于兼容旧浏览器。
  • 单应用代理沙箱(LegacySandbox):性能得到优化,但存在全局污染,同一时刻只能激活一个子应用。
  • 多应用代理沙箱(ProxySandbox):当前默认且最先进的方案。为每个子应用创建独立的虚拟window对象,实现真正的多实例共存,互不干扰。

3.4 qiankun 的 JavaScript 沙箱原理

JavaScript 沙箱:从“快照”到“代理”的演进。qiankun 的 JS 沙箱策略经历了三代演进,其核心矛盾在于如何在兼容性隔离性性能之间找到最佳平衡。当前版本会自动检测浏览器环境,在不支持 Proxy 时降级为快照沙箱。

演进阶段核心技术与适用场景优点缺点
快照沙箱 (SnapshotSandbox)无差别“复制-粘贴”,兼容所有浏览器- 原理简单 - 兼容性好- 性能差(全量遍历) - 仅支持单例
单例代理沙箱 (LegacySandbox)只记录变更,基于Proxy实现- 性能优化,无需全量遍历- 存在全局污染风险 - 仅支持单例
多例代理沙箱 (ProxySandbox)每个应用一个虚拟 Window 对象- 真正意义的隔离 - 支持多实例- 不兼容旧浏览器

3.5 qiankun 的 CSS 沙箱原理

CSS 沙箱:默认隔离与两种加强方案。qiankun 的 CSS 沙箱默认是自动开启的,其设计兼顾了开箱即用和灵活可控。

  1. 基础:动态样式隔离(默认开启)
    • 原理:利用 import-html-entry 解析子应用的 HTML 和 CSS,在卸载子应用时,主动移除其注入的所有样式表,保证应用切换时样式环境干净。
    • 局限:仅保证子应用间的样式互不影响,但无法完全隔离主子应用之间的样式冲突。
  2. 加强方案 1:严格样式隔离(strictStyleIsolation)
    • 实现:通过 strictStyleIsolation: true 开启,将整个子应用挂载到一个 Shadow DOM 节点下。
    • 原理:Shadow DOM 样式天然隔离,CSS 选择器无法穿透,是浏览器级别的隔离方案。
    • 代价:可能引发第三方 UI 库弹窗(如 Ant Design)找不到 body 而样式错乱等兼容性问题。
  3. 加强方案 2:实验性作用域沙箱(experimentalStyleIsolation)
    • 实现:通过 experimentalStyleIsolation: true 开启。
    • 原理:自动为子应用的所有 CSS 规则添加 div[data-qiankun-子应用名] 这样的属性选择器前缀,将其作用域限定在特定容器内。
    • 代价:可能因选择器权重增加,或弹窗逃逸出限定容器而产生样式意外。