共享依赖与避坑:shared / singleton / 何时别用微前端
本节目标
- 理解 shared 如何避免 React 被打包两份
- 知道 singleton 解决「多份 React 状态不互通」的坑
- 能列出微前端的常见翻车点与不适用场景
Module Federation 最易翻车的就是「共享依赖」。如果 remote 和 host 都打包了 React,浏览器会加载两份 React——不仅体积翻倍,更严重的是两份 React 各有各的上下文(Context/Hooks 状态),组件间状态不通、报错频发。
shared 配置就是解决这个:声明「这些依赖由一方提供、各方复用同一份」。
// host 和 remote 的 webpack.config.js 都配 shared
new ModuleFederationPlugin({
// ...
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' }
}
})singleton: true 的意思是「全局只允许一份 React」——无论谁先加载,后续方复用这同一份,避免双实例。requiredVersion 约定版本范围,版本差太多会告警。
常见翻车点清单:
- React 双实例:没配
singleton,状态不互通、Hooks 报错 → 必配shared.singleton。 - remote 先发、host 缓存了旧 remoteEntry:host 拉的是 CDN 缓存的旧地址 → 给 remoteEntry 加版本查询或「改 URL / 加
?v=N」绕缓存。 - 循环依赖:A 消费 B、B 又消费 A → 运行时死锁,重构边界。
- 公共依赖版本不一致:remote 用 lodash@4、host 用 lodash@5,shared 协商失败 → 统一版本或
shared锁定。
最后回到第一课的灵魂一问:什么时候别用微前端?
- 团队就一个、技术栈统一、发布节奏一致 → 普通单体 SPA 最省心;
- 项目小、迭代慢 → 微前端的基座/隔离/通信复杂度是纯负担;
- 没有「独立部署」诉求 → 那只是普通模块拆分,用 monorepo + 包管理就够了。
名词解释
shared(共享依赖):Module Federation 里声明「哪些依赖由应用间共用同一份」,避免每个应用都打包一份(如 React 被打包两次)。
singleton(单例):shared 的一个选项,强制「该依赖全局只有一份实例」,防止双 React 导致状态不通、Hooks 报错。
版本协商(Version Negotiation):remote 和 host 对 shared 依赖版本不一致时,Module Federation 按 requiredVersion 尝试匹配,匹配不上会告警或回退,常需手动对齐版本。
课后练习
练习 1:只写 shared: ['react'] 不写 singleton 够吗?
答案:不够稳。只列名字时 Module Federation 仍可能加载多份 React(不同构建各自带一份),只是「允许共享」。要彻底避免双实例必须加
singleton: true,这是生产环境必选项。
练习 2:微前端能解决「团队沟通差」吗?
答案:不能,反而放大。微前端解决的是「技术/部署层面的自治」,但接口契约、版本对齐、依赖共享仍需团队约定。组织问题不能靠架构甩锅——契约不清,微前端只会让 bug 分布得更散。
总结
Module Federation 的避坑核心就一句:shared 必配 singleton,否则双 React 状态不通。再加三道防线——remoteEntry 防缓存(改 URL/加 ?v=N)、避免循环依赖、统一公共依赖版本。最后把第一课的结论钉死:微前端是「独立部署诉求」的解法,没有这诉求就别上。八节课走完,你已能从「为什么」到「qiankun 接入」再到「Module Federation 共享」完整选型落地了。