← 中文资源

简体中文版本

React 水合不匹配 — 服务端与客户端 HTML 不一致

通过比较服务端 HTML 与首次客户端渲染,定位非确定性值、浏览器专属状态、非法嵌套或本地化差异,并修复水合错误。

阅读英文原文

症状

控制台报 “Hydration failed because the server rendered HTML didn’t match the client”;页面加载后立即闪烁或重新渲染;时间戳、随机值或主题在 view-source 与渲染后的 DOM 中不同。

诊断

用 curl -s https://example.com/page | grep -A2 -B2 'timestamp\|theme' 查看服务端输出。搜索组件中的非确定性来源:Math.random、Date.now()、new Date()、window、localStorage 在 render 期间执行都会导致两端不同。检查 hydration 错误附近的 HTML,常见问题是非法嵌套(如 div 在 p 内、button 在 button 内),浏览器会静默修复,从而改变 DOM。

根因

React 在 hydration 时复用服务端渲染的 DOM。如果首次客户端渲染产生不同标记,React 无法可靠挂载事件处理器并报告不匹配。差异来自非确定性值、render 阶段读取浏览器 API、非法 HTML 或环境相关的格式化。

修复

把浏览器专属值移到 useEffect 或挂载后初始化;为首次渲染使用稳定默认值;显式指定 locale 与时区;修复非法嵌套。suppressHydrationWarning 只用于有意且局部的差异,不能用来掩盖真正的不匹配。

// ❌ 服务端与客户端不同
function Clock() {
  return <span>{Date.now()}</span>;
}

// ✅ 首次渲染稳定,挂载后更新
function Clock() {
  const [now, setNow] = useState<number | null>(null);
  useEffect(() => setNow(Date.now()), []);
  return <span>{now ?? '—'}</span>;
}

验证

禁用缓存硬刷新,确认 hydration 警告消失;比较服务端 HTML 与客户端 DOM;禁用 JavaScript 查看服务端渲染内容;为组件添加相同 props 下服务端与客户端渲染一致的测试。

避免全局抑制

suppressHydrationWarning 适合单个有文档说明的可变文本节点,例如带降级值的时间戳。大范围使用会隐藏真实差异,增加后续调试难度。