创见博客
Next.js水合失败的常见场景
七崽爱吃小饼干2026/03/02阅读 1

Next.js 水合失败的常见场景

先一句话搞懂:水合是什么?

Next.js 先在服务端生成 HTML → 发给浏览器 浏览器拿到后,用 JS 让页面变成可交互(绑定事件、激活状态) 这个“激活”过程 = 水合(Hydration)

水合失败 = 服务端渲染的 HTML 和客户端渲染的不一致


一、最常见的 8 大水合失败场景

1. 客户端才有的 API 直接在组件顶层使用

直接在组件里写:

js
const isMobile = window.innerWidth < 768
  • 服务端没有 window / document
  • 服务端渲染:undefined
  • 客户端渲染:true/false 结果:HTML 对不上 → 水合爆炸

修复:

  • 放在 useEffect 里
  • 或用 dynamic + ssr: false

2. 使用 Date、时间、随机数等服务端/客户端不一致的值

js
// 服务端一个值,客户端一个值
const now = new Date().toISOString()
const random = Math.random()

服务端与客户端生成内容不一样 → 水合失败

修复:

  • useEffect 里获取
  • 或服务端传时间戳,客户端统一渲染

3. 条件渲染用了浏览器环境变量

js
return (
  <div>
    {typeof window !== 'undefined' && <ClientComp />}
  </div>
)

服务端渲染不显示,客户端显示 → DOM 结构不一致

修复: 用 useEffect 控制显示


4. 使用 localStorage / sessionStorage 直接渲染

js
const theme = localStorage.getItem('theme')

服务端没有 localStorage → 服务端 null,客户端有值 → 不一致

修复:

  • useEffect 里读取
  • 或使用 cookie + 服务端获取

5. 第三方库(富文本、图表、编辑器、轮播)

像:

  • Swiper
  • Chart.js
  • Editor.md / TinyMCE
  • D3.js 这些库依赖 window,服务端渲染不出来,客户端渲染出来 → DOM 不一致

修复:

js
import dynamic from 'next/dynamic'
const Editor = dynamic(() => import('@/components/Editor'), {
  ssr: false
})

6. 服务端与客户端数据不一致

例如:

  • 服务端获取数据 A
  • 客户端请求后变成数据 B 导致列表数量、内容不一样

修复: 确保服务端与客户端使用同一份初始数据 用 getServerSideProps / generateMetadata 统一获取


7. 多余/缺少空格、换行、注释导致不匹配

服务端:<div>hello</div>
客户端:<div> hello </div>

这种也会水合失败(React 18 严格模式)

修复: 保持结构完全一致


8. 使用 useState 初始值依赖浏览器环境

js
const [dark, setDark] = useState(window?.matchMedia('dark'))

服务端无 window → 初始值不一致

修复: useState 初始值设为 null/undefined,useEffect 里再更新


二、水合失败的典型报错

Error: Hydration failed because the initial UI does not match what was rendered on the server.
Text content does not match server-rendered HTML.
There was an error while hydrating. Because the error is non-serializable or was thrown earlier.

三、最快修复水合问题的万能公式

遇到水合报错,按这个顺序排查:

  1. 是否用了 window / document / navigator 直接渲染?
  2. 是否用了 new Date() / Math.random()?
  3. 是否用了 localStorage?
  4. 是否用了第三方UI库、图表、富文本?
  5. 是否条件渲染导致服务端客户端结构不同?

万能修复方案:

js
const [isClient, setIsClient] = useState(false)

useEffect(() => {
  setIsClient(true)
}, [])

if (!isClient) return null // 或服务端占位

四、给你总结

水合失败 = 服务端渲染的内容 ≠ 客户端渲染内容

最常见原因:

  1. 直接使用 window / document
  2. 时间、随机数
  3. 本地存储 localStorage
  4. 第三方客户端库
  5. 条件渲染不一致

解决口诀: 客户端逻辑,全部放进 useEffect! 第三方组件,全部用 dynamic + ssr: false!

评论
0/100