共享依赖隔离:多共享池(Share Scope)
把
shared分到多个命名的“共享池”(例如default、scope1),不同池之间互不共享。
背景
在 Module Federation 中,shared 默认都注册在 default 这个 share scope 下。当你的系统里存在以下情况时,单一 scope 往往不够用:
- 希望把一部分共享依赖“隔离”出来,避免与默认共享池互相影响(例如两套 React 生态并存、灰度升级、微前端子域隔离)。
- 希望同一个包在不同业务域使用不同版本/策略,但仍保持在各自域内共享(每个域内仍可 singleton / 复用)。
多共享池(share scope)的核心价值是:把共享依赖的注册与解析切到不同的命名空间(scope)里,从而实现共享池隔离与策略分层。
配置项速览
理解多共享池最简单的方法:只看“生产者怎么配”、“消费者怎么配”、“shared 条目落在哪个池里”,不需要了解运行时内部数据结构与变量名。
- 生产者(remote):用 shareScope 声明该 remote 会初始化哪些共享池(默认
default,支持string | string[])。 - 消费者(host):用 remotes[remote].shareScope 声明 host 与该 remote 需要对齐哪些共享池(默认
default)。 - shared 条目:用
shared[pkg].shareScope决定某个依赖注册/解析在哪个共享池(见 shared.shareScope)。
不同 shareScope 组合的效果
host 初始化某个 remote 时,会先根据双方的 shareScope 配置把“共享池”对齐(让 remote 知道哪些共享池要复用 host 的),随后 remote 再按自己的 shareScope 初始化共享依赖。
下面用 HostShareScope 表示 host(消费者)侧为某个 remote 配置的 remotes[remote].shareScope,用 RemoteShareScope 表示 remote(生产者)侧配置的 shareScope:
补充说明:
- 当某个 scope 在 host 的
shareScopeMap中不存在时,host 会先补齐为{}后再参与 init(因此不会出现 scopeName 找不到导致崩溃)。 - 多共享池下,remote 会尝试把 RemoteShareScope 中列出的 scope 都初始化到位。
构建插件如何配置
构建插件需要关注三个层面的 scope:
- Remote(生产者)侧:remote 自身要初始化哪些 scope(shareScope)。
- Host(消费者)侧:host 与某个 remote 对齐哪些 scope(remotes[remote].shareScope)。
- Shared(依赖条目)层:某个 shared 依赖注册在哪个 scope 下(
shared[pkg].shareScope)。
生产者
要点:
shareScope: ['default','scope1']决定 remoteEntry 在运行时会初始化哪些 scope。shared[pkg].shareScope决定该包最终注册/解析时使用哪个 scope;如果你把@company/design-system放在scope1,它将只在scope1的共享池中参与版本选择与复用。
消费者
要点:
remotes[remote].shareScope决定 host 运行时在 init 这个 remote 时,会把哪些 scope 作为shareScopeKeys传入 remote。- 如果 host 配了多 scope,但 remote 仍是单 scope,remote 会把 scopeMap 对齐好,但只会初始化单 scope 的 sharing(见上面的组合表)。因此多共享池要想“真正生效”,通常需要 host/remote 两侧都配置一致。
纯运行时(Runtime API)如何配置
当你不依赖构建插件 remotes(或希望在运行时动态注册 remotes/shared)时,可以用 runtime API 来完成同样的事情:
- remotes 的多 scope:使用
registerRemotes/createInstance({ remotes }),在 remote 配置里设置shareScope: string | string[]。 - shared 的 scope:使用
registerShared/createInstance({ shared }),在 shared 配置里设置scope: string | string[](注意这里字段名是scope)。
用 Runtime Hook 做更精细的控制
多 scope 的本质是“把共享池按名称分组”。当你希望更细粒度地控制 scope 的选择、对齐与回退策略时,可以使用 runtime hooks(runtime plugin)在 init 阶段或共享解析阶段介入。
1) 按 remote 动态改写 shareScopeKeys(beforeInitContainer)
下面的例子会让 legacy_remote 永远使用 legacy scope(即使它在构建期或运行时注册时写的是别的 shareScope):
2) scope 缺失时做别名/回退(initContainerShareScopeMap / resolveShare)
initContainerShareScopeMap:在 remote 初始化共享池过程中,对每个 scope 的shareScope映射做改写。resolveShare:在实际选择某个 shared 版本时介入,可用于“当前 scope 没有就回退到 default”等策略。
示例:如果 scope1 中找不到某个包,则回退用 default scope:
你也可以在 initContainerShareScopeMap 中把某个 scope 直接别名到另一个 scope(让两个 scope 共用同一个共享池):