Как обойти отсутствие доступа к DOM на сервере в Nuxt
Что проверяют
Понимаете ли вы, что setup компонента выполняется и на сервере, и в браузере, и знаете ли штатные средства Nuxt 3 и 4, а не обходы времён Nuxt 2. Следом обычно спрашивают про ошибки гидрации: откуда берутся и как их найти.
Как отвечать
Сначала механизм: при SSR код компонента выполняется в Node, где нет window, document и localStorage. Обращение к ним на верхнем уровне setup роняет рендер с ошибкой window is not defined. Способы обойти:
onMounted: хук вызывается только в браузере после монтирования. Здесь читают размеры окна, вешают слушатели, инициализируют библиотеки;<ClientOnly>: содержимое рендерится только на клиенте, а слот#fallbackпоказывает заглушку. Подходит для графиков, карт, редакторов;- суффиксы
.clientи.server:plugins/chart.client.tsподключится только в браузере, так же работают компоненты вродеMap.client.vue; import.meta.clientиimport.meta.server: флаги для условного кода.process.clientиз Nuxt 2 в новых версиях помечен устаревшим;- динамический импорт: библиотеку, которая трогает
windowпри импорте, загружают внутриonMountedчерезawait import(...); - правила маршрутов:
routeRulesсssr: falseотключают серверный рендер для части сайта, например админки.
Добавьте, что многие задачи вообще не требуют DOM: заголовок задают через useHead или useSeoMeta, общее состояние сервера и клиента через useState, а композаблы VueUse уже учитывают SSR.
Где спотыкаются
Рендерят на сервере и клиенте разное: Date.now(), Math.random(), ширину окна или данные из localStorage прямо в шаблоне. Разметка не совпадает, Vue сообщает о hydration mismatch, и часть интерфейса перерисовывается или ведёт себя странно.
Оборачивают в <ClientOnly> половину страницы. Ошибка уходит, но пропадает смысл SSR: контент не видит поисковик, а пользователь смотрит на пустые блоки.
Ставят проверку import.meta.client внутри функции, но импортируют библиотеку статически в начале файла. Если она обращается к window при загрузке, сервер упадёт ещё до проверки.