Module Federation 下 shared 父依赖的子依赖版本机制详解

123 阅读2分钟

问题

  • 父依赖(比如 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

流程如下:

  1. MF运行时host和remoteA通过shared机制,保证全局只会加载一份 packageA。
  2. 但 packageA 内部的 require('packageB'),resolve 依然走A打包时内部自己的 packageB@1.0.0。
  3. host 里的 packageB@2.0.0 不会被 remoteA/packageA 的代码自动用上。

可能带来的“坑”

  • 版本不一致:A内使用B@1,host自身是B@2,两者可能因API不兼容出问题,调试非常困难。
  • 副作用不可控:B如果有单例副作用,各remote/host各自有一份,无法做到全局唯一引用。
  • 难以维护:只有被shared的依赖才能全局唯一。

最佳实践建议

  1. 重要依赖/有副作用依赖必须同步写进shared
    • 比如react、mobx这些典型的全局单例副作用库,必须全局都写shared且严格版本控制。
  2. 如需递归共享,必须父子依赖都写入shared
    • 例如:shared: { packageA: {...}, packageB: {...} }
  3. 如果确实要多版本共存的包(比如polyfill、小功能库),可以不用shared。

补充:如果想让父子都完全一致怎么办?

  • 显式在shared里声明父&子依赖,总是确保全链路一致。
  • 如果子依赖非常多/不希望全部shared,则需要评估其副作用和兼容性(如工具型库可以不shared,状态单例类库必须shared)。

总结一句话

在Module Federation下,只有被列入shared的依赖才会全局唯一。未被shared的子依赖,父包用的就是父包本地的那个,不会自动继承host或别的remote的同名依赖!