什么是水合
所谓水合(Hydration),指的是 React 为预渲染的 HTML 添加事件处理程序,将其转为完全可交互的应用程序的过程。
React 提供了 hydrateRoot 客户端 API,通常搭配 react-dom/server 一起使用。先由 react-dom/server 生成 HTML,再调用 hydrateRoot 为生成的 HTML 进行水合,伪代码如下:
# 服务端
import { renderToString } from 'react-dom/server';
const html = renderToString(<App />);
# 客户端
import { hydrateRoot } from 'react-dom/client';
hydrateRoot(document.getElementById('root'), <App />);
与我们使用 React 时常用的 createRoot 不同,creatRoot 会重新创建 DOM 节点,而 hydrateRoot 会尽可能复用已有的 DOM 节点。
那具体是怎么进行水合的呢?
这就要说到 React 的渲染原理了。你可以这样简单粗暴的理解:
当调用 hydateRoot 的时候,会传入组件(例子中的 ),React 会据此构建 React 组件树,并按照 组件树的顺序遍历真实的 DOM 树,判断 DOM 树和组件树是否对应,如何对应,则跳过创建 DOM 节点的环节,复用当前 DOM 节点,添加事件并进行关联。
所以水合的前提是 DOM 树和组件树渲染一致。
常见的水合错误
- HTML 元素错误嵌套 
export default function App() {
  return (
    <p>
      text1
      <p>text2</p>
    </p>
  );
}- 渲染时使用 typeof window !== 'undefined' 等判断 
"use client";
export default function App() {
  const isClient = typeof window !== "undefined";
  return <h1>{isClient ? "Client" : "Server"}</h1>;
}- 渲染时使用客户端 API 如 window、localStorage 等 
"use client";
export default function App() {
  return (
    <h1>
      {typeof localStorage !== "undefined" ? localStorage.getItem("name") : ""}
    </h1>
  );
}- 使用时间相关的 API,如 Date.now() 
"use client";
export default function App() {
  return <h1>{Date.now()}</h1>;
}解决方法
- 是使用 useEffect 来处理: 
import { useEffect, useState } from "react";
export default function App() {
  const [time, setTime] = useState(Date.now());
  useEffect(() => {
    const timer = setInterval(() => {
      setTime(Date.now());
    }, 1000);
    return () => {
      clearInterval(timer);
    };
  }, []);
  return <h1>{time}</h1>;
}- 禁用特定组件的 SSR 渲染 
为什么会渲染不一致呢?本质上还是客户端组件既在服务端也在客户端渲染一份,干脆取消掉客户端组件的服务端渲染,为此需要借助 Next.js 提供的 dynamic 函数。
import dynamic from "next/dynamic";
export default function App() {
  const DynamicComponent = dynamic(() => import("./DynamicComponent"), {
    ssr: false,
  });
  return <DynamicComponent />;
}- 使用 suppressHydrationWarning 取消错误提示 
export default function App() {
  return (
    <div suppressHydrationWarning>
      {typeof window !== "undefined" ? "Client" : "Server"}
    </div>
  );
}- 自定义 hook 
最后更新于
这有帮助吗?