问题
- 父依赖(比如 packageA)被列入了
shared配置,因此各 MF 子应用和宿主会尝试共用一份 packageA。 - 子依赖(比如 packageA 依赖的 packageB)没有被 shared。
- 这个时候MF里面,最终“父依赖 packageA”实际上用的是哪一份“子依赖 packageB”?
结论 TL;DR
Module Federation 只对被 shared 配置的包做共享。父包被 shared 但它引用的子依赖(没被 shared),最终会用“父包构建时自己的 node_modules 里的那个版本”。
原理
- shared 配置只影响被列出来的包。Webpack MF shared 会把包注册在 runtime 的 sharedScope 池中,其他remote/host只要import就会共用一份。
- 子依赖(B)如果没被shared,A里写
require('b')时,resolve 路径依赖于A编译打包时的node_modules/b,不会去sharedScope里找host的或别的地方的B。 - MF不会"递归共享"。只有你明确在shared里声明的才走shared池。
实例说明
假设:
- 宿主(Host)有 packageB@2.0.0
- remoteA 建立了 shared: { packageA }(没有 packageB),A自己依赖的是 packageB@1.0.0
流程如下:
- MF运行时host和remoteA通过shared机制,保证全局只会加载一份 packageA。
- 但 packageA 内部的
require('packageB'),resolve 依然走A打包时内部自己的 packageB@1.0.0。 - host 里的 packageB@2.0.0 不会被 remoteA/packageA 的代码自动用上。
可能带来的“坑”
- 版本不一致:A内使用B@1,host自身是B@2,两者可能因API不兼容出问题,调试非常困难。
- 副作用不可控:B如果有单例副作用,各remote/host各自有一份,无法做到全局唯一引用。
- 难以维护:只有被shared的依赖才能全局唯一。
最佳实践建议
- 重要依赖/有副作用依赖必须同步写进shared
- 比如react、mobx这些典型的全局单例副作用库,必须全局都写shared且严格版本控制。
- 如需递归共享,必须父子依赖都写入shared
- 例如:
shared: { packageA: {...}, packageB: {...} }
- 例如:
- 如果确实要多版本共存的包(比如polyfill、小功能库),可以不用shared。
补充:如果想让父子都完全一致怎么办?
- 显式在
shared里声明父&子依赖,总是确保全链路一致。 - 如果子依赖非常多/不希望全部shared,则需要评估其副作用和兼容性(如工具型库可以不shared,状态单例类库必须shared)。
总结一句话
在Module Federation下,只有被列入shared的依赖才会全局唯一。未被shared的子依赖,父包用的就是父包本地的那个,不会自动继承host或别的remote的同名依赖!