先记住这个答案
React 会对部分 hydration 错误尝试恢复,但不同差异不保证采用同一种路径,属性差异也不保证全部修补。对于无法继续可靠接管的内容或结构不匹配,可以转为客户端渲染;存在合适 Suspense 边界时,恢复范围可能限于相关边界,否则可能扩大到根。恢复会增加客户端工作并可能替换节点,但不意味着之前已经展示的 SSR 首屏从未存在。应监控可恢复错误,同时修复导致首次输出不一致的根因。
- 属性差异不保证修补,也不等于必然整根回退
- 客户端恢复范围与边界位置有关
- 恢复成功不代表可以长期忽略错误
并非所有差异都有同一个修补结果
开发环境可以提示属性或文本不一致,但官方明确不承诺把所有属性差异逐项修正。不能看到界面大致正常就认为服务端属性已经与客户端预期完全一致,也不能把每条警告都当成整根重建证据。
从 React 18 开始,更严格的内容接管错误处理会在必要时放弃相关服务端子树并转向客户端渲染,最近合适的 Suspense 边界会影响范围。根外壳和边界内内容的风险不同,分析时应结合实际组件边界。
重建带来额外工作,但要准确描述时间线
用户可能已经看到了 SSR 正文,随后接管失败导致 React 在客户端重新生成某段界面。此前发生的可见首屏不会被追溯取消,真正新增的是重建耗时、可能的布局变化,以及交互接管进一步延后的成本。
节点替换可能影响焦点、选择范围或用户在接管前输入的内容,但不能宣称每次恢复都必然丢失这些状态。需要针对受影响的实际控件验证,尤其是弱网下用户先操作、脚本随后到达的场景。
监控恢复并回到首次输入修复
手动管理根的应用可以通过 hydrateRoot 的 onRecoverableError 收集自动恢复事件,并结合错误类型和组件栈定位。它不是所有警告的统一入口,也不应把原始用户数据、完整敏感 URL 或无筛选的状态快照直接上报。
框架通常管理根和错误机制,应使用其提供的接入方式,避免为增加监控再创建第二个根。修复后还要复测原先的数据版本、浏览器环境和交互路径,确认诊断消失且节点与事件行为符合预期。
容易答错的地方
- 认为恢复会自动重新请求所有数据
- 客户端重建组件不等于必然重新发起全部请求。是否请求取决于数据层、缓存和组件副作用,分析网络成本应以真实请求记录为准,不能把某种数据实现的行为归结为 hydration 固有规则。
- 把没有报错当作属性已经修补
- 生产诊断形式和开发环境不同,某些不一致也不一定进入可恢复错误回调。应检查关键 DOM 属性和交互结果,同时修复初始输入来源,不能靠控制台安静来证明接管正确。
面试官还会怎么问?
多加 Suspense 能代替修复不匹配吗?
不能。边界可以影响隔离和恢复范围,但不会让错误的初始数据变得一致。应围绕真实加载与交互边界设计 Suspense,并修复内容差异,不能为压住错误随意包裹每一个元素。
提前调用 root.render 为什么会丢掉 SSR 内容?
在根尚未完成 hydration 时主动更新,React 可能清除服务端内容并将整个根切换到客户端渲染。这与普通完成接管后的状态更新不同,应避免启动期间绕过框架额外抢先渲染。
怎样验证恢复范围而不是猜测?
在固定 React 版本的最小复现中分别把差异放在根外壳和 Suspense 子树,记录诊断并检查节点身份、焦点及实际交互。将这些观察限制在对应版本与场景,不能把一次结果概括成所有差异的协议。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。