MFE微前端:共享模块的那些事

1.怎么避免主应用和远程应用的依赖重复加载?

        避免远程项目(remote)和主项目(host)的依赖重复加载,是微前端(MFE)架构中的核心优化之一。下面是结合 Angular + Webpack Module Federation 场景中的原理具体配置方法


为什么会重复加载依赖?

  • 微前端子应用和主应用都是独立构建的,它们各自有自己的 node_modules 和依赖包。
  • 如果没有合理共享配置,主子项目都会打包自己的依赖,导致重复加载,体积变大,运行时资源浪费。
  • 共享依赖失败还会带来版本冲突和错误。

避免重复加载的核心:共享依赖(shared modules)

Webpack Module Federation 提供 shared 配置,声明哪些依赖是“共享”的,确保它们只加载一次。


具体做法(Angular + Webpack)

1. 配置 ModuleFederationPlugin 的 shared

主应用和子应用的 webpack.config.js 中,都要声明共享依赖,通常是:

shared: {
  '@angular/core': { singleton: true, strictVersion: true, requiredVersion: 'auto' },
  '@angular/common': { singleton: true, strictVersion: true, requiredVersion: 'auto' },
  '@angular/router': { singleton: true, strictVersion: true, requiredVersion: 'auto' },
  'rxjs': { singleton: true, strictVersion: true, requiredVersion: 'auto' },
  // 如果有其他公共依赖,也一并声明
}
  • singleton: true:保证整个微前端应用中,这个依赖只有一个实例被加载和使用。
  • strictVersion: true:严格校验版本,避免版本不兼容。
  • requiredVersion: 'auto':自动从 package.json 读取版本号,保证主子应用版本一致。

2. 主项目配置示例
new ModuleFederationPlugin({
  name: 'hostApp',
  remotes: {
    mfe1: 'mfe1@http://localhost:4201/remoteEntry.js',
  },
  shared: {
    '@angular/core': { singleton: true, strictVersion: true, requiredVersion: 'auto' },
    '@angular/common': { singleton: true, strictVersion: true, requiredVersion: 'auto' },
    '@angular/router': { singleton: true, strictVersion: true, requiredVersion: 'auto' },
    'rxjs': { singleton: true, strictVersion: true, requiredVersion: 'auto' },
  },
});

3. 远程项目配置示例
new ModuleFederationPlugin({
  name: 'mfe1',
  filename: 'remoteEntry.js',
  exposes: {
    './Module': './src/app/mfe1/mfe1.module.ts',
  },
  shared: {
    '@angular/core': { singleton: true, strictVersion: true, requiredVersion: 'auto' },
    '@angular/common': { singleton: true, strictVersion: true, requiredVersion: 'auto' },
    '@angular/router': { singleton: true, strictVersion: true, requiredVersion: 'auto' },
    'rxjs': { singleton: true, strictVersion: true, requiredVersion: 'auto' },
  },
});

4. 保证版本完全一致
  • 主子项目 package.json 中依赖版本必须保持一致。
  • 使用相同的 Angular 和 rxjs 版本。
  • 通过 npm ls rxjsnpm ls @angular/core 等命令确认依赖树。

5. 打包和运行时
  • 共享依赖会优先从主应用中加载(如果已经加载了该依赖),
  • 子应用不会重复加载,而是复用主应用的版本,避免了体积膨胀。

6. 其他建议
  • 共享依赖范围可以根据项目需要扩展,比如 lodashmoment 等通用库。
  • 生产环境开启打包优化(Tree shaking + Scope Hoisting)能进一步减小包体积。
  • 如果有版本冲突,及时统一版本或者调整 strictVersion 配置。

特别注意

  strictVersion: false 并不能保证不重复加载依赖,它的作用主要是“放宽版本校验”,让共享依赖版本不严格匹配时依然能加载,但是否重复加载要看其他配置。

1. strictVersion 的作用
  • strictVersion: true:表示要求共享依赖的版本必须严格匹配 requiredVersion,否则报错或不共享。

  • strictVersion: false:允许版本不严格匹配,只要大致兼容就共享,避免因版本差异导致的冲突或错误。

2. 依赖是否重复加载的关键是 singleton
  • singleton: true 是保证同一个共享模块全局只加载一份实例的关键配置。

  • 只要设置了 singleton: true,不管版本是否严格匹配,webpack 会尽量复用同一个版本。

3. strictVersion: false 的风险
  • 版本不严格匹配时会尝试共享,减少冲突报错,但如果主子应用依赖版本差异较大,运行时可能出现兼容性问题。

  • 并不是“不重复加载”的保障,而是“版本冲突时更宽容”的配置。

结论
配置项 是否保证不重复加载? 作用
singleton: true 保证只加载一个共享实例
strictVersion: true 否,版本不匹配会报错或失败共享 严格校验版本,保证兼容性
strictVersion: false 否,宽松版本匹配,不保证兼容 避免因版本不符导致共享失败或报错
建议
  • 一定要设置 singleton: true,保证共享依赖不会重复加载。
  • 版本允许范围内,最好开启 strictVersion: true,保持版本一致性,避免运行时问题。
  • 只有在明确知道版本兼容且不想因版本冲突失败时,才考虑用 strictVersion: false

总结

关键点 说明
shared 配置 通过 Module Federation 配置共享依赖
singleton: true 保证依赖只加载一次
strictVersion: true 严格版本控制,避免兼容问题
版本一致性 主子应用必须使用完全相同版本
自动版本获取 requiredVersion: 'auto'

2. 在一个微前端项目中,对于同一个依赖,远程应用strictVersion: false,主应用是strictVersion: true会有什么影响呢?

如果主应用 strictVersion: true,而子应用 strictVersion: false,这个共享依赖的最终加载行为由主应用决定,但 这种不一致的配置 会造成难以预料的兼容性和调试问题,建议主子项目的 singletonstrictVersionrequiredVersion 配置 保持完全一致


具体影响分析

主项目设置 子项目设置 结果 / 影响
strictVersion: true strictVersion: true ✅ 推荐配置:主子应用必须版本完全一致,否则构建或运行时报错。
strictVersion: true strictVersion: false ⚠️ 子应用不报错,但主项目会在版本不匹配时阻止共享,仍然会报错或共享失败。
strictVersion: false strictVersion: true ⚠️ 主项目放松限制,可能共享了子应用不兼容的版本,导致运行时异常。
strictVersion: false strictVersion: false ✅ 宽松配置:不会因为版本不一致而报错,但风险在于运行时版本冲突或行为不一致。

原因简述

Webpack Module Federation 的共享模块解析是这样做的:

  • 运行时(不是构建时)决定是否共享某模块
  • 首次加载某个 shared module(比如主项目加载了 [email protected]),它会注册为共享模块。
  • 当其他子项目尝试加载 rxjs 时,会比较自己的要求(requiredVersionstrictVersion)是否接受这个已经加载的版本。

如果主项目是 strictVersion: true,要求加载的子模块版本 完全一致,但子项目用了 strictVersion: false,它会默认“我能接受任何版本”,但主项目说“不行,我必须是这个版本”,于是冲突产生。


✅ 最佳实践

保持主项目和所有子项目的共享依赖配置一致:

shared: {
  rxjs: {
    singleton: true,
    strictVersion: true,
    requiredVersion: deps['rxjs'], // 或写死相同版本
  }
}

这样可以:

  • 保证所有共享依赖加载一致版本;
  • 避免版本冲突或行为异常;
  • 减少调试成本和部署出错风险。

3. “运行时版本冲突或行为不一致”是什么意思?

        在微前端架构里,主项目和子项目共享一个依赖(比如 rxjs),但它们的版本号不一样,或者共享配置松散(strictVersion: false),导致同一个模块可能被加载了多个不同版本,或者模块的内部状态不兼容。


具体表现和问题举例:

1. 多个版本的同一个库同时存在

后果:

  • 主应用和子应用其实用了两个不同的 rxjs 实例,内存占用翻倍;
  • 如果它们之间传递 Observable 或 Subject 对象,可能导致类型不匹配、事件不触发或者异常。
2. 单例依赖状态不一致
  • 很多库(尤其 Angular 核心包、rxjs)依赖全局单例,比如共享状态、缓存、订阅管理。
  • 版本不一致会让状态分裂,比如事件订阅在一个版本生效,另一个版本监听不到,导致 UI 反应异常或者数据流断裂。
3. API 不兼容导致运行时错误
  • 低版本没有高版本的新 API 或者行为有变更,导致调用接口时发生报错,比如函数不存在或者参数格式不对。
  • 这种错误很难在编译时发现,只能运行时崩溃,影响用户体验。

总结

现象 说明
多个版本同时加载 资源浪费,内存占用加倍
状态不一致 事件、订阅等核心功能异常
API 兼容性差导致崩溃 程序运行时报错,功能异常
难以调试 运行时错误不容易定位到版本冲突问题

怎么避免?

  • 共享依赖都设置 singleton: true,只加载一份实例。
  • 保持版本一致,严格版本匹配(strictVersion: true)。
  • 版本更新时同步主子项目依赖版本,避免断层。

你可能感兴趣的:(MFE微前端:共享模块的那些事)