Module Federation 高频面试题 + 满分答案

一、Module Federation 是什么?【★★★★★】

核心思路(一句话)

Module Federation 解决的是"多个独立项目之间怎么复用模块"的问题,而且这个模块可以在运行时从另一个项目加载,而不是构建时就打进自己的 Bundle。

1. 举例说明

假设公司有两个完全独立的前端项目:

text 复制代码
项目 A
www.a.com

项目 B
www.b.com

项目 B 做了一个非常成熟的 DatePicker。

传统做法:

text 复制代码
项目 B
  ↓
npm publish
  ↓
项目 A npm install
  ↓
Webpack 构建
  ↓
DatePicker 被打进项目 A 的 Bundle

Module Federation 可以变成:

text 复制代码
项目 A
  ↓
用户访问 A
  ↓
运行时加载 B 的模块
  ↓
直接使用 B 提供的 DatePicker

所以它解决的不是单纯的"代码复用"。因为 npm 本身就能代码复用。真正有区别的是:

text 复制代码
npm
模块复用发生在构建阶段

Module Federation
模块复用可以发生在运行阶段

2. 技术定义

Webpack Module Federation 允许多个独立构建在运行时提供和消费模块,并且可以对公共依赖进行共享。

这里有三个关键词:

text 复制代码
独立构建
   ↓
运行时
   ↓
模块提供 / 消费

3. 三个层次不要混

这是面试时非常重要的一点。

text 复制代码
Module Federation
│
├── 解决的问题
│     多个独立项目之间如何复用模块
│
├── 核心能力
│     运行时提供 / 消费远程模块
│
└── 关键实现
      远程模块的解析和加载可以延迟到运行时

所以不要简单回答:

"Module Federation 就是代码共享。"

也不要简单回答:

"Module Federation 就是运行时加载。"

前者太宽泛,后者只是实现特征。最准确的表达是:

价值是独立构建之间的模块共享,特点是可以运行时提供和消费远程模块,关键实现是把远程模块的解析和加载延迟到运行时。


二、为什么需要 Module Federation?【★★★★★】

核心思路(一句话)

因为大型系统中经常存在多个独立构建的项目,而传统 npm 共享模块必须经过"发布 → 安装 → 构建 → 部署",Module Federation 可以把远程模块的使用延迟到运行时。

传统 npm

text 复制代码
公共项目
   ↓
npm publish
   ↓
业务项目 npm install
   ↓
Webpack 构建
   ↓
模块进入业务项目 Bundle
   ↓
部署

如果公共项目更新:

text 复制代码
公共项目 v1
     ↓
发布 v2
     ↓
业务项目仍然使用 v1

业务项目必须重新:

text 复制代码
npm install
   ↓
重新构建
   ↓
重新部署

Module Federation

text 复制代码
公共项目
   ↓
构建 Remote
   ↓
部署 remoteEntry.js
        ↓
业务项目运行
        ↓
运行时加载 Remote
        ↓
获取模块

因此:

text 复制代码
Remote 更新
   ↓
重新部署 Remote
   ↓
Host 不需要因为 Remote 的业务代码变化而重新构建

注意:这不是说 Host 永远不需要重新构建。

如果 Host 自己修改了代码、Remote 地址、版本策略、共享依赖配置等,仍然可能需要重新构建。


三、Module Federation 和 npm 有什么区别?【★★★★★】

核心思路(一句话)

npm 是构建时共享,Module Federation 是运行时共享。

npm Module Federation
模块获取 构建时 运行时
模块是否进入 Host Bundle 通常会 Remote 模块不会直接进入 Host Bundle
Remote 更新 通常需要 Host 更新依赖并重新构建 可以独立更新 Remote
发布方式 npm 包 Remote 独立部署
典型特点 版本固定、依赖明确 独立部署、运行时加载

最容易被追问的问题

"那 npm 也能代码共享,Module Federation 到底特殊在哪里?"

npm 解决的是"把别人写好的代码拿到我的项目里,然后一起构建 ";Module Federation 解决的是"我的项目已经构建完成以后,运行时还能去另一个独立构建中获取模块"。


四、Host、Remote、Shared 分别是什么?【★★★★★】

核心思路(一句话)

Host 是使用方,Remote 是提供方,Shared 是多个构建之间需要共同使用的依赖。

text 复制代码
             Module Federation

                  Host
             使用远程模块
                  │
                  │
                  ↓
               Remote
             提供远程模块

          ┌───────────────┐
          │     Shared    │
          │ React / ReactDOM │
          └───────────────┘

Host

消费远程模块的应用。

例如:

javascript 复制代码
import Button from 'designSystem/Button';

这里的业务应用就是 Host。

Remote

对外提供模块的独立构建。

例如:

javascript 复制代码
exposes: {
  './Button': './src/Button.jsx'
}

表示:

text 复制代码
Remote 对外提供:

designSystem/Button
        ↓
src/Button.jsx

Shared

Shared 解决的是:

Host 和 Remote 都依赖 React 时,如何避免各自带一份 React,以及如何处理版本问题。

注意:

text 复制代码
Remote 模块共享
≠
Shared 依赖共享

这是两个问题。


五、Remote 到底是什么?【★★★★☆】

核心思路(一句话)

Remote 不是一个特殊的"子应用",而是一个能够对外提供模块的独立 Webpack 构建。

例如:

text 复制代码
App B
│
├── src
│   ├── Button.jsx
│   └── DatePicker.jsx
│
└── Webpack
      ↓
remoteEntry.js
      ↓
对外提供 Button / DatePicker

因此:

Remote 的本质是"模块提供方"。

它不一定是微前端子应用。甚至两个完全无关的网站:

text 复制代码
www.a.com
www.b.com

也可以:

text 复制代码
B 提供 DatePicker
A 运行时加载 DatePicker

这仍然是 Module Federation。


六、remoteEntry.js 是什么?【★★★★★】

核心思路(一句话)

remoteEntry.js 是 Remote 对外暴露模块的运行时入口,Host 先找到它,才能进一步找到 Remote 提供的具体模块。

例如 Remote:

javascript 复制代码
new ModuleFederationPlugin({
  name: 'appB',

  filename: 'remoteEntry.js',

  exposes: {
    './DatePicker': './src/DatePicker.jsx'
  }
})

构建后:

text 复制代码
dist/
├── remoteEntry.js
├── xxx.js
└── xxx.chunk.js

Host:

javascript 复制代码
remotes: {
  appB: 'appB@https://b.com/remoteEntry.js'
}

运行时:

text 复制代码
Host
 ↓
https://b.com/remoteEntry.js
 ↓
Remote Container
 ↓
找到 ./DatePicker
 ↓
加载 DatePicker 对应 Chunk
 ↓
返回模块

注意

remoteEntry.js 不是 DatePicker 本身。

它更像:

text 复制代码
Remote 的"运行时入口 + 模块获取入口"

它里面包含 Webpack Runtime 和 Container 相关代码,用来告诉 Host:

text 复制代码
我有哪些模块可以提供
这些模块怎么获取
需要哪些 Chunk
怎么初始化 Shared

七、exposes 和 remotes 有什么区别?【★★★★★】

核心思路(一句话)

exposes 是"我提供什么",remotes 是"我要从谁那里拿什么"。

Remote:

javascript 复制代码
exposes: {
  './Button': './src/Button.jsx',
  './Table': './src/Table.jsx'
}

意思:

text 复制代码
我提供:

Button
Table

Host:

javascript 复制代码
remotes: {
  designSystem: 'designSystem@https://cdn.xxx.com/remoteEntry.js'
}

意思:

text 复制代码
我知道 designSystem 在哪里

然后:

javascript 复制代码
import Button from 'designSystem/Button';

就是:

text 复制代码
Host
 ↓
designSystem
 ↓
Button

八、Module Federation 运行时到底是怎么加载远程模块的?【★★★★★】

这是高级面试最值得展开的一题。

核心思路(一句话)

Host 发现自己需要远程模块后,由 Webpack Runtime 动态加载 Remote 的 remoteEntry,再通过 Container 获取暴露模块;如果模块对应的 Chunk 还没加载,就继续动态加载 Chunk。

完整流程

text 复制代码
业务代码
  │
  │ import('appB/DatePicker')
  ↓
Webpack Runtime
  │
  │ 加载 remoteEntry.js
  ↓
Remote Container
  │
  │ container.init(...)
  ↓
初始化 Shared
  │
  │ container.get('./DatePicker')
  ↓
寻找 DatePicker
  │
  │
  ├── 模块已经存在
  │       ↓
  │     直接返回
  │
  └── 模块所在 Chunk 未加载
          ↓
       动态加载 Chunk
          ↓
       Chunk 执行
          ↓
       注册模块
          ↓
       container.get()
          ↓
       返回 DatePicker

九、import('appB/DatePicker') 为什么浏览器能识别?【★★★★☆】

核心思路(一句话)

浏览器根本不认识 appB/DatePicker,这是 Webpack Module Federation 在构建阶段识别的模块标识,最终由 Webpack Runtime 完成远程加载。

浏览器原生理解的是:

javascript 复制代码
import('./DatePicker.js')

而:

javascript 复制代码
import('appB/DatePicker')

不是浏览器直接解析的 URL。Webpack 在构建时知道:

text 复制代码
appB
 ↓
https://b.com/remoteEntry.js

于是把它转换成 Federation Runtime 的远程模块加载逻辑。所以:

text 复制代码
源码
 ↓
import('appB/DatePicker')
 ↓
Webpack 构建
 ↓
生成 Federation Runtime
 ↓
浏览器执行 Runtime
 ↓
加载 remoteEntry.js

十、container.get() 和 container.init() 是干什么的?【★★★★★】

核心思路(一句话)

init() 负责初始化共享依赖环境,get() 负责获取 Remote 暴露出来的具体模块。

可以把 Container 理解成:

javascript 复制代码
const container = {
  init(shareScope) {
    // 初始化共享依赖
  },

  get(moduleName) {
    // 获取远程模块
  }
}

实际 Webpack 生成代码会复杂得多,这里是理解它的核心模型。运行:

javascript 复制代码
await container.init(
  __webpack_share_scopes__.default
);

然后:

javascript 复制代码
const factory = await container.get('./DatePicker');

const DatePicker = factory();

所以:

text 复制代码
init()
 ↓
先处理 Shared

get('./DatePicker')
 ↓
再获取具体 Remote Module

十一、Shared React 到底放在哪里?【★★★★★】

核心思路(一句话)

shared 不是把 React 放进一个公共文件夹,而是让不同 Webpack Runtime 在运行时通过 Share Scope 协调"谁提供 React、谁复用 React、使用哪个版本"。

例如:

javascript 复制代码
shared: {
  react: {
    singleton: true
  },
  'react-dom': {
    singleton: true
  }
}

概念上可以理解为:

text 复制代码
Share Scope
│
├── react
│   ├── 18.2.0
│   └── 18.3.1
│
└── react-dom
    ├── 18.2.0
    └── 18.3.1

Runtime 会根据配置和版本要求选择可用版本。

最重要的一点

text 复制代码
Remote 模块共享
        ↓
container.get()

React 依赖共享
        ↓
Share Scope

这是两条不同的链。


十二、为什么 Shared React 不会出现 React is undefined?【★★★★☆】

核心思路(一句话)

因为 Webpack 会把共享依赖的初始化和远程模块执行组织成异步依赖关系,Remote 不是拿到代码后立刻无条件执行,而是在需要 Shared 时先完成相应初始化。

概念流程:

text 复制代码
加载 Remote
    ↓
container.init(shareScope)
    ↓
建立 Shared 依赖环境
    ↓
container.get('./Button')
    ↓
Button 执行
    ↓
获取 React
    ↓
React.createElement(...)

因此不是:

text 复制代码
下载 Button
 ↓
立刻执行 Button
 ↓
突然发现 React 不存在

而是:

text 复制代码
准备依赖
 ↓
初始化共享环境
 ↓
获取模块
 ↓
执行模块

不过不能说它"绝对不会 undefined"。

如果存在:

text 复制代码
Remote 加载失败
Shared 配置错误
React 版本不兼容
初始化失败
运行时冲突

依然可能报错。


十三、singleton: true 是什么意思?【★★★★★】

核心思路(一句话)

singleton 的核心目的是让一个 Share Scope 中的依赖尽量只使用一个共享实例,React 这种要求单实例的库尤其重要。

例如:

javascript 复制代码
shared: {
  react: {
    singleton: true
  },
  'react-dom': {
    singleton: true
  }
}

为什么 React 特别需要关注?

因为如果:

text 复制代码
Host
 └── React A

Remote
 └── React B

可能出现:

text 复制代码
React Context
Hooks
Provider
Component

跨不同 React 实例工作的问题。尤其可能导致经典错误:

text 复制代码
Invalid hook call

所以实际项目通常会对 React / ReactDOM 采用 singleton 策略。


十四、如果 Host 和 Remote 的 React 版本不一样怎么办?【★★★★☆】

核心思路(一句话)

Webpack 会根据 Shared 配置和版本要求进行共享依赖选择,但"能不能安全共存"最终取决于版本兼容性和项目配置,不能简单理解成自动解决所有版本冲突。

例如:

text 复制代码
Host
React 18.3

Remote
React 18.2

可以通过 Shared 配置声明版本要求。

但如果:

text 复制代码
Host
React 18

Remote
React 17

就不能只靠:

javascript 复制代码
singleton: true

认为问题自动消失。需要考虑:

text 复制代码
版本兼容
requiredVersion
strictVersion
singleton
实际依赖关系

十五、Remote 更新以后,为什么 Host 可以不重新构建?【★★★★★】

核心思路(一句话)

因为 Host Bundle 里保存的是"去哪里找 Remote",而不是 Remote 的完整业务代码。

传统 npm:

text 复制代码
Host
 ↓
npm install Remote v1
 ↓
Remote v1 进入 Host Bundle
 ↓
Host 部署

Remote 更新:

text 复制代码
Remote v2
 ↓
Host Bundle 里面还是 v1

所以必须重新构建。

Module Federation:

text 复制代码
Host
 ↓
知道 Remote 地址
 ↓
https://b.com/remoteEntry.js

Remote:

text 复制代码
remoteEntry.js
 ↓
DatePicker
 ↓
对应 Chunk

因此:

text 复制代码
Remote 更新
 ↓
Remote 重新部署
 ↓
Host 下次运行时加载新的 Remote

但是注意:

"不需要重新构建 Host"不等于"自动永远拿最新版本"。

还会受到:

text 复制代码
浏览器缓存
CDN 缓存
remoteEntry 缓存策略
版本化 URL
灰度发布
兼容性

影响。


十六、为什么 remoteEntry.js 可以更新,而 Host 不需要重新构建?【★★★★☆】

核心思路(一句话)

因为 Host 保存的是 Remote 的访问地址,而不是 Remote 的具体代码。

例如:

javascript 复制代码
remotes: {
  appB: 'appB@https://b.com/remoteEntry.js'
}

Host 保存的是:

text 复制代码
https://b.com/remoteEntry.js

不是:

text 复制代码
DatePicker 的源代码

所以:

text 复制代码
Host
 │
 │ 地址没变
 ↓
remoteEntry.js
 │
 │ 内容已经更新
 ↓
新版本 Remote Module

这就是为什么:

text 复制代码
Remote 可以独立部署

十七、Module Federation 的完整运行流程是什么?【★★★★★】

这是建议直接背下来的流程图。

text 复制代码
                    用户访问 Host
                         │
                         ↓
                 Host Bundle 执行
                         │
                         ↓
             import('appB/DatePicker')
                         │
                         ↓
                Webpack Runtime
                         │
                         ↓
             加载 remoteEntry.js
                         │
                         ↓
                 Remote Container
                         │
                         ↓
                container.init()
                         │
                         ↓
                 初始化 Share Scope
                         │
                         ↓
             container.get('./DatePicker')
                         │
                         ↓
              找到 DatePicker 对应 Chunk
                         │
                         ↓
                  动态加载 Chunk
                         │
                         ↓
                   Chunk 执行
                         │
                         ↓
                 注册 Remote Module
                         │
                         ↓
                获取 Shared React
                         │
                         ↓
                 执行 DatePicker
                         │
                         ↓
                    React 渲染

一句话串起来

Host 运行 → 请求 Remote Entry → 初始化共享依赖 → 获取暴露模块 → 按需加载对应 Chunk → 获取共享 React → 执行远程模块。


十八、Webpack Module Federation 和微前端是什么关系?【★★★★★】

核心思路(一句话)

微前端是一种架构思路,Module Federation 是一种模块运行时共享技术,两者不是同一个东西。

text 复制代码
微前端
│
└── 解决"大型前端系统怎么拆分和独立交付"

Module Federation
│
└── 解决"独立构建之间怎么运行时共享模块"

可以:

text 复制代码
微前端
  +
Module Federation

但也可以:

text 复制代码
普通 Webpack 项目
  +
Module Federation

例如:

text 复制代码
公司设计系统团队
       ↓
提供 Button / Table / DatePicker
       ↓
Module Federation
       ↓
多个完全独立的业务网站消费

这些网站不需要组成一个微前端应用。

所以:

Module Federation 可以作为微前端的技术实现之一,但 Module Federation 本身不是微前端。


十九、Module Federation 和动态 import 有什么区别?【★★★★☆】

核心思路(一句话)

动态 import 解决的是"当前构建中的代码什么时候加载",Module Federation 解决的是"另一个独立构建提供的代码怎么在运行时加载"。

动态 import:

javascript 复制代码
import('./UserPanel')

通常是:

text 复制代码
当前项目
 ↓
Webpack 构建
 ↓
UserPanel 被拆成 Chunk
 ↓
运行时动态加载 Chunk

Module Federation:

javascript 复制代码
import('appB/UserPanel')

是:

text 复制代码
当前项目
 ↓
发现这是 Remote
 ↓
加载 Remote Entry
 ↓
找到 Remote Module
 ↓
加载对应 Chunk

所以:

text 复制代码
dynamic import
= 当前 Bundle 内的代码分包加载

Module Federation
= 跨独立构建的运行时模块加载

二十、Module Federation 和普通 Webpack Code Splitting 有什么区别?【★★★★☆】

核心思路(一句话)

Code Splitting 是一个应用自己的代码拆成多个 Chunk,Module Federation 是多个独立构建之间共享和加载模块。

Code Splitting:

text 复制代码
App
 ├── main.js
 ├── user.js
 └── product.js

这些 Chunk:

text 复制代码
都属于同一个构建

Module Federation:

text 复制代码
App A
   ↓
App B 的 remoteEntry
   ↓
App B 的 Chunk

这里:

text 复制代码
A 和 B
属于不同构建

这是本质区别。


二十一、Module Federation 如何处理缓存问题?【★★★★☆】

核心思路(一句话)

remoteEntry 负责找到 Remote,真正业务 Chunk 通常可以使用内容哈希做长期缓存,而 remoteEntry / manifest 则需要重点设计更新策略。

典型生产策略:

text 复制代码
remoteEntry.js
↓
较短缓存 / 合理缓存策略

xxx.[contenthash].js
↓
长期缓存

因为:

text 复制代码
remoteEntry
负责"告诉我新版本在哪里"

contenthash Chunk
负责"真正的代码内容"

如果 remoteEntry 长时间被 CDN 缓存:

text 复制代码
Remote 已经发布 v2
       ↓
用户仍然拿到 v1 remoteEntry
       ↓
仍然使用旧版本

因此生产环境需要认真设计:

text 复制代码
缓存
版本
CDN
灰度
回滚
兼容性

二十二、Module Federation 有哪些问题?【★★★★★】

核心思路(一句话)

Module Federation 最大的代价是把原本构建阶段发现的问题,部分推迟到了运行阶段。

主要问题:

1. 运行时失败

text 复制代码
remoteEntry 加载失败
Chunk 加载失败
CDN 故障
网络异常

以前:

text 复制代码
构建失败

现在可能变成:

text 复制代码
线上运行失败

2. 版本兼容

text 复制代码
Host v1
Remote v2

接口、React、公共依赖可能不兼容。


3. 调试复杂

传统:

text 复制代码
代码
 ↓
构建
 ↓
Bundle

Module Federation:

text 复制代码
Host
 ↓
Remote Entry
 ↓
Remote Chunk
 ↓
Shared
 ↓
不同 Runtime

排查链更长。


4. 部署耦合变成运行时契约

虽然:

text 复制代码
独立部署

但不是:

text 复制代码
完全没有依赖

Host 仍然依赖:

text 复制代码
Remote 地址
Remote 暴露模块
模块 API
Shared 依赖
版本兼容

因此:

独立部署不等于完全解耦。


二十三、如果 Remote 加载失败怎么办?【★★★★☆】

核心思路(一句话)

把 Remote 当成一个运行时外部依赖处理,必须有超时、降级、监控和版本回滚策略。

例如:

text 复制代码
Host
 ↓
加载 Remote
 ↓
失败?
 ├── 否 → 正常渲染
 │
 └── 是
      ↓
   重试
      ↓
   仍然失败
      ↓
   降级 UI
      ↓
   上报监控

例如:

javascript 复制代码
async function loadRemote() {
  try {
    return await import('appB/ProductDetail');
  } catch (error) {
    reportError(error);

    return {
      default: () => '商品详情暂时不可用'
    };
  }
}

生产环境还可以增加:

text 复制代码
超时
重试
备用 Remote
版本回滚
错误监控
熔断 / 降级

二十四、如何设计 Module Federation 的版本管理?【★★★★☆】

核心思路(一句话)

不要把 Remote 当成"永远只有一个最新版本",而应该把 Remote 当成有版本的运行时依赖。

例如:

text 复制代码
Remote
├── v1
├── v2
└── v3

配合:

text 复制代码
Manifest
 ↓
版本选择
 ↓
灰度
 ↓
回滚

生产环境重点考虑:

text 复制代码
Remote 版本
Shared 版本
兼容性
缓存
灰度
回滚

二十五、Module Federation 最容易被问的"源码原理题"【★★★★★】

如果面试官继续往底层追,可以记住这条链:

text 复制代码
import('appB/Button')
        ↓
Webpack Runtime
        ↓
Remote Loading
        ↓
加载 remoteEntry.js
        ↓
Remote Container
        ↓
container.init()
        ↓
Share Scope
        ↓
container.get('./Button')
        ↓
加载 Button Chunk
        ↓
Webpack Chunk Runtime
        ↓
注册 Module
        ↓
返回 Module Factory
        ↓
执行 Module

Webpack Runtime 中常见的核心概念:

text 复制代码
__webpack_require__
__webpack_require__.f
__webpack_require__.l
webpackChunk...
__webpack_share_scopes__
container.init()
container.get()

不需要一上来背源码。真正应该理解的是:

text 复制代码
谁负责加载?
    ↓
Webpack Runtime

加载什么?
    ↓
remoteEntry.js

谁提供模块?
    ↓
Remote Container

怎么拿模块?
    ↓
container.get()

公共依赖怎么处理?
    ↓
Share Scope

具体代码在哪里?
    ↓
Remote Chunk

二十六、完整最小示例

Remote:提供 DatePicker

javascript 复制代码
// webpack.config.js

const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  mode: 'production',

  plugins: [
    new ModuleFederationPlugin({
      // Remote 在运行时的名字
      name: 'appB',

      // Remote 对外提供的运行时入口
      filename: 'remoteEntry.js',

      // 对外暴露模块
      exposes: {
        // 外部使用:
        // appB/DatePicker
        //
        // 实际对应:
        // ./src/DatePicker.jsx
        './DatePicker': './src/DatePicker.jsx'
      },

      // 与 Host 共享 React
      shared: {
        react: {
          singleton: true
        },

        'react-dom': {
          singleton: true
        }
      }
    })
  ]
};

Host:消费 DatePicker

javascript 复制代码
// webpack.config.js

const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  mode: 'production',

  plugins: [
    new ModuleFederationPlugin({
      name: 'appA',

      // 告诉 Webpack:
      // appB 的 remoteEntry 在哪里
      remotes: {
        appB: 'appB@https://b.com/remoteEntry.js'
      },

      // 与 Remote 共享 React
      shared: {
        react: {
          singleton: true
        },

        'react-dom': {
          singleton: true
        }
      }
    })
  ]
};

业务代码:

javascript 复制代码
import React, { Suspense } from 'react';

// appB/DatePicker 不是浏览器直接解析的 URL。
// Webpack Module Federation 会把它识别成 Remote Module。
const DatePicker = React.lazy(
  () => import('appB/DatePicker')
);

export default function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <DatePicker />
    </Suspense>
  );
}

二十七、把整个例子串起来

text 复制代码
                App A
                  │
                  │
                  │ import('appB/DatePicker')
                  ↓
          Webpack Runtime
                  │
                  │
                  ↓
       https://b.com/remoteEntry.js
                  │
                  ↓
            Remote Container
                  │
                  ├───────────────┐
                  │               │
                  ↓               ↓
            container.init()   container.get()
                  │               │
                  ↓               ↓
             Share Scope      DatePicker
                  │               │
                  ↓               ↓
                React         DatePicker Chunk
                                  │
                                  ↓
                            执行模块代码
                                  │
                                  ↓
                            使用共享 React
                                  │
                                  ↓
                              React 渲染

这张图就是 Module Federation 的核心。


二十八、面试追问链

建议按照下面的顺序准备:

text 复制代码
① Module Federation 是什么?
        ↓
② 它解决什么问题?
        ↓
③ 和 npm 有什么区别?
        ↓
④ 为什么可以运行时加载?
        ↓
⑤ Host / Remote 是什么?
        ↓
⑥ remoteEntry.js 是什么?
        ↓
⑦ exposes / remotes 是什么?
        ↓
⑧ import('appB/Button') 怎么实现?
        ↓
⑨ container.get() 做什么?
        ↓
⑩ container.init() 做什么?
        ↓
⑪ shared 是什么?
        ↓
⑫ React 到底共享在哪里?
        ↓
⑬ singleton 为什么重要?
        ↓
⑭ React 版本冲突怎么办?
        ↓
⑮ Remote 更新为什么 Host 可以不重新构建?
        ↓
⑯ 缓存怎么办?
        ↓
⑰ Remote 加载失败怎么办?
        ↓
⑱ 和微前端什么关系?
        ↓
⑲ 和 dynamic import / Code Splitting 什么区别?
        ↓
⑳ 生产环境怎么做版本、灰度、回滚?

二十九、最终"满分答案"

简单说,Module Federation 解决的是多个独立项目之间怎么复用模块的问题。传统 npm 是在构建阶段把依赖安装到项目里,再一起打包;Module Federation 则允许一个项目构建完成以后,在运行时从另一个独立构建中加载模块。

Webpack 中,提供模块的一方叫 Remote,使用模块的一方叫 Host。Remote 通过 exposes 暴露模块,Host 通过 remotes 找到 Remote 的 remoteEntry.js。运行时 Host 先加载 Remote Entry,再通过 Container 的 init() 初始化共享依赖,通过 get() 获取暴露模块,如果模块对应的 Chunk 没有加载,就继续动态加载 Chunk,最后执行远程模块。

另外,shared 解决的是公共依赖共享,比如 Host 和 Remote 都使用 React 时,可以通过 Share Scope 和 singleton 等配置尽量保证使用兼容的共享 React 实例。

所以理解 Module Federation 最关键的是区分三件事:它的价值是独立构建之间共享模块,它的核心特点是运行时提供和消费远程模块,它的关键实现是把远程模块的解析和加载从构建阶段延迟到了运行阶段。它可以用于微前端,但本身不等于微前端。


三十、100 分评分标准

如果面试题是:

"你讲一下 Webpack Module Federation。"

60 分

只回答:

"Module Federation 是 Webpack 的模块联邦,可以实现模块共享。"

问题:

  • 太宽泛
  • 没说和 npm 的区别
  • 没说运行时
  • 没说独立构建

75 分

能说:

text 复制代码
Host
Remote
Shared
exposes
remotes
remoteEntry

说明知道基本配置。


85 分

能讲清:

text 复制代码
npm 构建时共享
        VS
Module Federation 运行时共享

并能解释:

text 复制代码
remoteEntry
container.get
container.init
Chunk

95 分

还能继续解释:

text 复制代码
Share Scope
singleton
React 版本
缓存
Remote 更新
运行时失败

并且能够把整个加载过程串起来。


100 分

最终能把下面这条因果链讲完整:

text 复制代码
为什么需要?
        ↓
独立构建之间需要共享模块

为什么不用 npm?
        ↓
npm 主要是构建时共享

Module Federation 特殊在哪里?
        ↓
运行时提供 / 消费远程模块

怎么做到?
        ↓
Remote Entry + Container + Webpack Runtime

具体怎么拿?
        ↓
container.get()

依赖怎么办?
        ↓
Share Scope + Shared

为什么能独立部署?
        ↓
Host 不把 Remote 业务代码直接打进自己的 Bundle

代价是什么?
        ↓
把一部分问题从构建阶段推到了运行阶段

生产怎么办?
        ↓
版本 + 缓存 + 监控 + 降级 + 灰度 + 回滚

这一条链真正理解了,Module Federation 这类题基本就是一个完整的技术方案。

这套题里,最值得重点背的是第 1、3、8、11、15、17、18、29 题。它们基本构成了面试官从"概念"一路追到"底层原理和生产实践"的完整链路。

相关推荐
码字的字节1 年前
深入探讨Hadoop YARN Federation:架构设计与实践应用
hadoop·yarn·federation
16年上任的CTO2 年前
一文大白话讲清楚webpack进阶——9——ModuleFederation实战
前端·webpack·node.js·模块联邦·federation
16年上任的CTO2 年前
一文大白话讲清楚webpack进阶——8——Module Federation
前端·webpack·node.js·模块联邦·federation