学习目标
理解什么是 SSR Error、什么是 Hydration Error、两者有什么区别,以及为什么
onMounted()可以避免这些问题。
一、这个知识解决什么问题?
上一章我们已经知道 SSR → 显示 HTML → Hydration 这条流程。
那为什么有时候页面不报错,却会出现:
Hydration completed but contains mismatches.
或者:
Hydration node mismatch
到底什么是 Hydration Error?
二、一起推导
案例一:SSR Error
const width = window.innerWidth
服务器有没有 window?没有。 所以服务器直接报错 ReferenceError,连 HTML 都生成不了:
SSR → window → ReferenceError → HTML 都没有返回
Hydration 根本还没有开始,所以这不是 Hydration Error,而是 SSR Error。
案例二:真正的 Hydration Error
const width = ref(0)
if (import.meta.client) {
width.value = window.innerWidth
}
- 服务器:
width = 0,生成<div>0</div> - 浏览器:执行
window.innerWidth,得到1920 - Vue 想接管:服务器
0≠ 浏览器1920
Vue 在 Hydration 时检测到不一致,报 Hydration Mismatch。
这才是真正的 Hydration Error。
三、SSR Error 和 Hydration Error 的区别
| SSR Error | Hydration Error | |
|---|---|---|
| 发生时间 | 服务器生成 HTML 时 | 浏览器开始接管 HTML 时 |
| 现象 | 服务器直接崩溃(ReferenceError) | 服务器与浏览器结果不一致 |
| HTML 返回了吗 | ❌ 没有 | ✅ 有 |
| 典型例子 | 直接访问 window |
服务器输出 0,浏览器计算出 1920 |
四、为什么 onMounted() 不会报错?
const width = ref(0)
onMounted(() => {
width.value = window.innerWidth
})
执行顺序:
服务器 → 生成 HTML(width = 0)
↓
浏览器显示(width = 0)
↓
Hydration(0 = 0,一致!✅)
↓
onMounted() → 修改 width = 1920(正常响应式更新)
第一轮服务器和浏览器都是 0,Hydration 成功。之后 onMounted 修改值是正常响应式更新,已经不是 Hydration,所以不会报错。
五、脑图
以后遇到相关错误,按三个问题依次判断:
- 服务器能否执行完成? 如果访问了不存在的
window,会在生成 HTML 前成为 SSR Error。 - 服务器是否已经返回 HTML? 没有 HTML 就不可能进入 Hydration。
- 客户端首次计算结果是否与服务器一致? 不一致才是 Hydration Mismatch。
onMounted() 能把浏览器专属读取推迟到 Hydration 之后,但页面可能先显示默认值再更新。若该值影响首屏结构,还需考虑 <ClientOnly> 等方案。
六、容易踩坑
❌ window 导致的一定是 Hydration Error
不是。服务器直接执行 window,首先发生的是 SSR Error。
❌ onMounted 属于 SSR
不是。它发生在 Hydration → Mounted 之后,可以安全使用 window、document、localStorage。
一句话记忆
SSR Error 是服务器崩了;Hydration Error 是服务器和浏览器"对不上答案"。