Гидрация и hydration mismatch
↑ 7.5 SSR и Nuxt · ← Предыдущая · → Следующая
Зачем это на собесе
Hydration mismatch — самая частая проблема SSR; важно уметь объяснить причину и починить.
Гидрация
Сервер отдаёт HTML, клиент запускает то же приложение и сопоставляет vnode с существующим DOM, не пересоздавая его, добавляет обработчики и реактивность.
Mismatch
Ошибка возникает, когда дерево на клиенте отличается от серверного HTML.
Частые причины:
| Причина | Пример |
|---|---|
| Данные, зависящие от среды | window, localStorage, ширина экрана |
| Время и случайность | new Date(), Math.random(), Date.now() |
| Таймзона и локаль | разный формат даты на сервере и клиенте |
| Невалидный HTML | <p> внутри <p>, <div> в <p>, браузер меняет структуру |
| Разные данные | сервер и клиент получили разные ответы API |
| Расширения браузера | вставляют узлы в DOM |
Как чинить
<script setup lang="ts">
const now = ref('')
onMounted(() => { now.value = new Date().toLocaleString() }) // только на клиенте
</script>
<template>
<ClientOnly> <!-- Nuxt -->
<ChartWidget />
<template #fallback><Skeleton /></template>
</ClientOnly>
</template>- состояние, зависящее от браузера, инициализировать в
onMounted; - передавать серверное состояние клиенту (Nuxt
useState, Piniastateвwindow.__INITIAL_STATE__); - время и локаль форматировать в одной таймзоне;
useId()для стабильных идентификаторов;data-allow-mismatch(Vue 3.5) точечно подавляет предупреждение.
Ленивая гидрация (Vue 3.5)
const Comments = defineAsyncComponent({
loader: () => import('./Comments.vue'),
hydrate: hydrateOnVisible(), // гидрируем, когда блок попал в viewport
})Также hydrateOnIdle, hydrateOnInteraction, hydrateOnMediaQuery.
Стоимость
Гидрация выполняет весь JS ещё раз: «uncanny valley» — страница видна, но не отвечает. Уменьшают бандл, ленивая гидрация, острова.
Вопросы с ответами
Что такое hydration mismatch?
Расхождение между серверным HTML и тем, что рендерит клиент. Vue выдаёт предупреждение, а иногда пересоздаёт узлы, что даёт мигание и баги.
Как безопасно использовать window в SSR?
Только на клиенте:
onMounted, проверкиimport.meta.client/typeof window, компонентClientOnly.