背景与目标
昨天的核心问题:React 实例重复(远程主题各自带 React,与宿主不一致,导致 hooks/上下文失效)。
今天的目标:远程主题共享宿主 React 实例,并把首屏链路、时序与缓存都调顺。
核心方案总览
-
React 实例单点来源
-
远程主题不再自带 React。
-
import map 将
react与react/jsx-runtime指向宿主侧的 bridge 代理。 -
代理使用 Proxy 懒绑定:只有真正访问 API 时才从
window.__REACT__/window.__REACT_JSX__取值,避免时序开箱即炸。
-
-
时序与可见性保证
-
ReactBridge(客户端组件)在
useEffect中把宿主的React/jsx-runtime赋值到window,这是副作用,放在 effect 更符合语义。 -
Bridge 赋值后
dispatchEvent('react-ready')对外广播“可用了”。 -
远程加载流程在真正
import(url)前await waitForReactGlobal():-
事件优先唤醒,
requestAnimationFrame轮询兜底,可设超时/可中断。 -
本质是一个 Promise,把“React 注入就绪”变成可等待的条件。
-
-
-
页面装配方式
-
在 RootLayout 的
body顶部挂 ReactBridge(最早可用的位置)。 -
在具体
page里再去加载远程组件(在 loader 内部已await waitForReactGlobal())。 -
import map scopes(可选强化):仅对 CDN 前缀生效,确保宿主自身 import 不被代理污染。
-
构建与一致性
-
vendor manifest 升级
-
远程产物的 manifest 新增
"versions",记录react/react-dom/react/jsx-runtime的官方版本号; -
"files"仅保留需要从 CDN 拉的部分(如react-dom/react-dom/client等)。
-
-
import map 构建策略
-
react、react/jsx-runtime→ bridge 代理(宿主提供); -
react-dom、react-dom/client(以及可选scheduler)→ CDN 路径。 -
版本校验:在构建 import map 之前,比较“宿主本地包版本”与 “vendor manifest.versions”。
-
Dev:不一致直接抛错;Prod:仅记录错误日志并继续(不中断业务)。
-
-
性能与网络
-
modulepreload精准化-
只预热
react-dom/client(“钥匙带大门”),让首个远程 ESM 链路更快进入可执行态。 -
react/jsx-runtime已走宿主代理,无需 CDN 预热。 -
可加
preconnect到 CDN,抢先建链路。
-
-
ETag 与强缓存友好
-
远程产物文件名包含官方版本 + hash;
-
请求使用
cache: 'no-cache'+ ETag,让“未变更 304 / 已变更更新”逻辑清晰。
-
正确性要点(踩坑清单→反坑方案)
-
竞态:任何在 ReactBridge 赋值前执行的
import 'react'都会炸 → 通过waitForReactGlobal()统一“就绪条件”,并让 proxy 惰性取值。 -
CSP:避免
new Function,原生 dynamic import 加忽略注释,规避unsafe-eval。 -
宿主污染:使用 scopes 限定 import map 只影响 CDN 来源模块。
-
版本漂移:manifest 与宿主包版本比对,Dev fail-fast,Prod 打点报警。
-
可演进性:proxy 使用
Reflect.get保持 accessor/原型链/符号键行为一致,兜底未来 API。
当前落地状态
-
✅ 远程主题组件已成功加载并共享宿主 React。
-
✅ 首屏链路稳定:Bridge → ready 事件 → loader 等就绪 → import 远程模块。
-
✅ 版本一致性与预热策略闭环。
-
✅ 方案不绑定框架特性,可复用于多主题/多租户/按需上线的场景。
后续可选优化(展望)
-
更细颗粒的
modulepreload:根据远程入口 URL 自动生成预热列表。 -
运行时观测:对版本不一致、加载失败、超时的路径埋点上报。
-
RSC 兼容轨道:在 server 侧补“远程清单预计算与签名”,为后续 RSC/SSR 组合打底。
一句话总结:
我们通过 import map + bridge + Proxy 懒绑定 + 时序等待,把远程主题的 React 依赖切换为“宿主单例”,配合 版本校验 和 精准预热,从根上解决了“React 实例重复”的老大难问题,并把整条加载链路收敛成可维护、可观测、可演进的工程系统。
