当前位置: 首页 > news >正文

Template-system 之 增强远程加载组件的稳定性

思路:底层用Map做全局缓存(为了幂等)+ 重试,表层用React-query做真正缓存

底层用 Map 做全局幂等(single-flight),中间加 可判定的重试(retry with backoff),表层用 react-query 做状态与缓存编排

我们要解决的真实问题

  1. 并发加载:同一个远程 URL,在页面上可能被多个组件几乎同时请求。不能重复发请求,更不能出现“一半成功、一半失败”的状态撕裂。

  2. 脆弱网络:CDN 抖动、浏览器缓存污染、历史前进/后退与 importmap 的交互……需要“可以重试但不瞎重试”。

  3. 状态编排:React 侧要有 loading/error/cached 的统一语义,还要能长期缓存(URL 带版本 hash 时,等价于“永不过期”)。

  4. 契约约束:远程导出的组件可能带 schema。我们希望在入口就把 校验做了,防止“参数半错半对”把锅甩给下游 UI。

上面四条,分别由:Map 幂等 → Retry → react-query → AJV 校验 对应承接。


1) 底座:为什么用 Map 做全局幂等(single-flight)

幂等=相同输入只执行一次。在浏览器里,一个最自然的“幂等单元”就是 Promise
于是我们在 window.__REMOTE_IMPORT_CACHE__ 上挂了一个 Map<string, Promise<Module>>

  • Key 用“模块 URL”(一般包含版本哈希),天然把“版本”与“缓存”绑定起来;

  • Value 是一次真实的动态导入 Promise。并发时,其他请求直接复用这条 Promise;

  • 全局作用域意味着跨组件、跨调用点都能复用,不用在每个地方写“去重逻辑”。

这样做的结果是:多处同时发起同一个 URL 的加载,只会真正 import 一次。其余地方等这一次结果就行。
这既避免了重复网络开销,也避免了“有的拿到了 A 实例,有的拿到了 B 实例”的诡异分叉。

设计取舍:为什么不是 WeakMap?
我们需要以 URL 字符串 为 key(不是对象),并希望缓存贯穿页面生命周期,WeakMap 不合适。内存回收交给上层(react-query 的 gcTime)做生命周期管理。


2) 重试应该“聪明”,不应该“执拗”

不是所有失败都值得重试。我们把错误分成两类:

  • 结构性错误(不可重试)

    • export "X" not found(说明导入契约不匹配)

    • SyntaxError / Unexpected token(产物坏了或被非 ESM 缓存覆盖)

    • react family / version mismatch(运行时不兼容)
      这类错误,无论重试多少次也没意义——应立即把错误冒泡出来。

  • 瞬时错误(可重试)

    • CDN 短暂 5xx;

    • 浏览器缓存竞态;

    • importmap-shim 在历史前进/后退时解析抖一下。
      这类错误,指数退避 + 抖动能明显提高成功率:例如 1s → 2s → 4s,并在每步加一点随机抖动,上限控制在 10s 以内。

错误判定 放在 底层 loader(而不是 react-query)里,有两个好处:

  • 去重 + 重试可以绑在同一条 Promise 上(single-flight 的同伴们共享命运);

  • 策略高度内聚,上层不用关心“哪些错能重试、哪些不能”。


3) 为什么还要等“平台就绪”(waitForReactGlobal

远程模块往往依赖运行时桥(如 React/JSX runtime、importmap-shim、宿主注入的服务等)。
竞态很常见:如果你比这些桥更早去 import 远程包,失败概率会直线上升(甚至落到不可重试的结构性失败)。

所以我在 loader 的第一步就做了就绪门禁

  • 先判断(flag/probe),比如全局变量与注入标记;

  • 再监听(事件,如 react-ready / ss-services:ready);

  • 必要时加 rAF 兜底,并支持 超时/AbortSignal

把这一步放在 loader 内部而非调用方,能保证“所有路径都走一遍门禁”,减少 80% 的玄学失败


4) 动态导入与 CSP:为什么用 importShim

  • 直接 new Function()/eval 在很多站点会触发 CSP: unsafe-eval

  • 原生 import() 在“浏览器后退 + importmap-shim”场景下偶尔解析不稳;

  • 因此我选了 importShim(与 importmap-shim 配套)作为统一入口,让运行时解析规则更可控

  • 同时标注 /* webpackIgnore */ /* @vite-ignore */,确保它真的是运行时加载,而不是被打包器吞掉。


5) react-query 只是“表层编排”,但它很关键

底层 loader 已经具备:幂等 + 重试 + 就绪门禁
为什么还要 react-query?因为它擅长做状态与生命周期这件事:

  • 缓存键['remoteComponent', url, exportName],与远程产物一一对应;

  • staleTime: Infinity:URL 通常带版本哈希,视为“永不过期”;

  • gcTime:没人订阅后,延迟回收这条查询,避免页面来回切换时反复拉取;

  • retry: false:底层已重试,表层不再二次重试,避免“叠罗汉”;

  • refetchOnWindowFocus: false:远程组件不该因为“切回标签页”就突然刷新;

  • networkMode: 'always':不与浏览器的“离线/在线”状态耦合(交由底层策略判断)。

一句话:react-query 负责“把它当状态管理”——loading、error、data 的语义化,以及缓存生命周期的管理。


6) 契约守护:为什么做 schema 校验(AJV 2020)

远程导出的模块,既可以是一个 React 组件,也可以是一个 { component, schema } 的对象。
如果给了 schema,我们就地做 JSON Schema 校验

  • 只要拿到 schema,就尝试对 validateProps 进行校验;

  • WeakMap 缓存编译好的 validator(以 schema 对象地址为 key),避免重复编译;

  • 严格模式(dev):有 schema 但没传 props,可抛错帮助发现问题;

  • 生产环境降为警告/日志,不让线上 UI 崩溃

这一步把“契约正确性”提前到了“加载阶段”,减少“UI 层模糊错误”。


7) 为什么不是 Context?为什么不是 Redux?

  • Context:跨页面、跨远程包时容易出现“实例不一致”——Provider 与 Consumer 不是同一个 Context 身份,值会丢。且 Context 是树形作用域,容易被多 root/portal 搞出边界问题。

  • Redux:强调可序列化状态与时间旅行。把“服务能力”(函数、类、DOM 对象)塞进去,既违背约定,也破坏 DevTools。

所以:服务用 Service Registry(全局单例)状态用 react-query/Redux局部策略用 Context 覆写。各司其职,系统更稳定。


8) 失败模式与处理原则

  • export "X" not found → 契约不匹配,不要重试,尽快把可读的错误抛给上层;

  • 语法错误 / 版本冲突 → 产物或运行时不兼容,不要重试

  • 网络/解析瞬时失败 → 可重试,指数退避并设置上限;

  • 平台未就绪 → 先判定、再监听;尽量别让它变成“重试类错误”。

错误越“可判定”,重试策略越“克制”。这是稳定系统的关键。


9) 观察性与排障(建议一起上)

  • 每次加载记录:URL、import.meta.url、版本、时延、是否 retry、最终状态;

  • 就绪事件里带 detail:version / provided / ts

  • 控制台仅在 dev 打印堆栈与 schema 误用,prod 输出简洁结构化日志;

  • 为 loader 本身加一个小型“断路器”(一段时间内失败率爆表时短暂停止,避免雪崩)。


10) 小结:三层同心圆

  • 内环(Loader):Map 幂等 + 智能 Retry + 就绪门禁(importShim + 事件 + rAF)

  • 中环(Cache):react-query 负责状态与生命周期(stale/gc/订阅)

  • 外环(Contract):可选 schema 校验,尽早暴露契约问题

这三层使得“远程组件”这件看似混沌的事,变得可预期、可恢复、可维护

http://www.sczhlp.com/news/73448/

相关文章:

  • 平台门户网站建设兰州网络推广服务合同
  • 宣城网站建设 有限公司手机网站建设分析
  • 如何建立一个视频网站百度网盟推广怎么做
  • win2008怎么做网站网页设计与网站建设 作业
  • 企业网站开发 语言 收录融资网站建设方案
  • 绍兴建设企业网站做网站多少钱西宁君博美评
  • 做网站是用什么语言升学历的正规机构官网
  • 工程建设定额云南效果好的网站优化
  • 兰州中川国际机场电话贵阳百度快照优化排名
  • 南通网站建设知识wordpress 移动端 接口
  • 长春网站排名推广个人购物网站建设
  • 上海电子通科技网站建设西海岸新区城市建设局网站
  • 网站建设服务网络服务外贸零售平台
  • 沈阳网站建设与开发大兴安岭网站建设
  • 中型网站网站开发 介绍 回扣
  • 男男做爰视频网站建设网站需要多少费用
  • 厦门专业建站系统制作公司word发布到wordpress
  • 搜索引擎的网站有哪些企业oa系统是什么
  • 科技广告公司网站模板建设一个网站可以放视频的多少钱
  • 发布课程的网站模板农村做网站赚钱
  • iis html网站微信怎么做小程序的
  • 重庆网站维护公司网页模板源码
  • ps做网站logo尺寸做封面怎么把网站加上去
  • 网站开发最好代理公司经营范围
  • 做网站分类模块的设计思路asp.net个人网站
  • 深圳网站制作公司流程电子商务网站建设与维护期末答案
  • 网站怎么做实名认证吗网站建设竞争对数分析
  • 东莞网站系统找哪里谁可以做开码网站
  • 可以直接进入的正能量网站老狼公司网站如何优化
  • phpcms多个网站易申建设网站