Гидратация¶
30.09.2026
Что такое гидратация¶
Гидратация восстанавливает на клиенте приложение, которое уже отрисовал сервер. Сюда входит переиспользование серверных структур DOM, сохранение состояния приложения, передача данных, которые сервер уже получил, и другие процессы.
Зачем нужна гидратация¶
Гидратация ускоряет приложение: не нужно заново создавать узлы DOM. Angular сопоставляет уже существующие элементы DOM со структурой приложения во время выполнения и по возможности переиспользует узлы. Выигрыш измеряют статистикой Core Web Vitals (CWV): меньше задержка первого ввода (FID), отрисовка самого крупного содержимого (LCP) и совокупное смещение макета (CLS). Эти цифры влияют и на SEO.
Без гидратации приложение Angular после серверного рендеринга уничтожает DOM и рисует его заново, и интерфейс может заметно мигнуть. Повторная отрисовка бьёт по Core Web Vitals вроде LCP и вызывает смещение макета. С гидратацией готовый DOM переиспользуется, и мигания нет.
Как включить гидратацию в Angular¶
Гидратация доступна только приложениям с серверным рендерингом (SSR). Сначала включите серверный рендеринг по руководству Angular по SSR.
Через Angular CLI¶
Если SSR включали через Angular CLI — при создании приложения или позже командой ng add @angular/ssr, — код гидратации уже должен быть в приложении.
Ручная настройка¶
В своей сборке, где SSR включали не через Angular CLI, гидратацию добавляют вручную. В корневом компоненте или модуле импортируют provideClientHydration из @angular/platform-browser и кладут этот провайдер в список провайдеров старта приложения.
1 2 3 4 5 6 7 8 9 | |
Если приложение на NgModule, provideClientHydration добавляют в список провайдеров корневого модуля.
1 2 3 4 5 6 7 8 9 10 | |
Вызов provideClientHydration() должен входить и в набор провайдеров, которым приложение стартует на сервере. В проекте со структурой по умолчанию (команда ng new) достаточно добавить вызов в корневой AppModule: этот модуль импортирует серверный модуль. В своей сборке добавьте provideClientHydration() в список провайдеров серверного старта.
Проверка, что гидратация включена¶
После настройки гидратации и запуска сервера откройте приложение в браузере.
До полной гидратации, скорее всего, придётся поправить прямую работу с DOM: перейти на конструкции Angular или поставить ngSkipHydration. Подробнее — в разделах Ограничения, Прямая работа с DOM и Как пропустить гидратацию отдельных компонентов.
В режиме разработки гидратацию подтверждают в консоли инструментов разработчика браузера. Там будет сообщение со статистикой гидратации, например числом компонентов и узлов. Angular считает статистику по всем компонентам страницы, включая компоненты сторонних библиотек.
Статус гидратации компонентов на странице показывает и расширение Angular DevTools. В DevTools можно включить оверлей: он отмечает, какие части страницы гидратированы. Если есть ошибка расхождения гидратации, DevTools подсветит компонент, который её вызвал.
Захват и повтор событий¶
Когда приложение отрисовано на сервере, оно видно в браузере сразу после загрузки HTML. Пользователь может решить, что со страницей уже можно работать, но слушатели событий появятся только после гидратации. Начиная с v18, повтор событий (Event Replay) захватывает все события до гидратации и проигрывает их, когда гидратация закончилась. Включают его функцией withEventReplay():
1 2 3 4 5 | |
Как работает повтор событий¶
Повтор событий улучшает опыт: пользовательские события, которые случились до конца гидратации, захватываются и затем проигрываются, так что взаимодействие не теряется.
Повтор событий делится на три фазы:
- Захват действий пользователя. До гидратации Event Replay перехватывает и сохраняет все взаимодействия: клики и другие нативные события браузера.
- Хранение событий. Контракт событий (Event Contract) держит в памяти всё, что записано на предыдущем шаге, чтобы потом это можно было проиграть.
- Повторный запуск событий. Когда гидратация завершена, Angular заново вызывает захваченные события.
Повтор событий поддерживает нативные события браузера, например click, mouseover и focusin. Библиотека JSAction, на которой построен повтор, описана в readme.
Так действия пользователя до гидратации не пропадают, и опыт остаётся цельным.
Если включена инкрементальная гидратация, повтор событий включается автоматически.
Ограничения¶
С гидратацией у приложения появляются ограничения, которых без неё нет. Сгенерированная структура DOM на сервере и на клиенте должна совпадать. Гидратация ждёт одинаковое дерево DOM в обоих местах. Сюда входят пробелы и узлы-комментарии, которые Angular создаёт при отрисовке на сервере. Эти пробелы и узлы должны быть в HTML, который отдал серверный рендеринг.
HTML, который получился при серверном рендеринге, нельзя менять между сервером и клиентом.
Если деревья DOM сервера и клиента расходятся, гидратация не сможет сопоставить ожидаемое с тем, что реально есть в DOM. Чаще всего виноваты компоненты, которые меняют DOM напрямую через нативные API.
Прямая работа с DOM¶
Если компонент меняет DOM нативными API или через innerHTML и outerHTML, гидратация завершится ошибкой. Типичные случаи: обращение к document, поиск конкретных элементов и вставка дополнительных узлов через appendChild. Отсоединение узлов и перенос их в другое место тоже даёт ошибку.
Angular не знает об этих изменениях DOM и не может их разрешить во время гидратации. Он ждёт одну структуру, а встречает другую. Расхождение срывает гидратацию и выбрасывает ошибку несовпадения DOM (см. ниже).
Такую работу с DOM лучше убрать из компонента и сделать через API Angular. Если переписать поведение пока нельзя, поставьте атрибут ngSkipHydration (описан ниже), пока не появится решение, совместимое с гидратацией.
Корректная структура HTML¶
Если в шаблоне компонента некорректная структура HTML, при гидратации возможна ошибка несовпадения DOM.
Самые частые случаи:
<table>без<tbody><div>внутри<p><a>внутри другого<a>
Если неясно, валиден ли HTML, проверьте его валидатором синтаксиса.
Стандарт HTML не требует элемент <tbody> внутри таблиц, но современные браузеры сами создают <tbody> в таблицах, где его не объявили. Из-за этого расхождения всегда явно объявляйте <tbody> в таблицах, иначе гидратация упадёт с ошибкой.
Настройка сохранения пробелов¶
С гидратацией лучше оставлять значение по умолчанию false для preserveWhitespaces. Если параметра нет в tsconfig, значение и так false, ничего менять не нужно. Если включить сохранение пробелов через preserveWhitespaces: true в tsconfig, с гидратацией возможны проблемы. Эта конфигурация пока поддерживается не полностью.
Значение должно быть одинаковым в tsconfig.server.json для сервера и в tsconfig.app.json для браузерной сборки. Разные значения ломают гидратацию.
Если параметр всё же задают в tsconfig, его лучше поставить только в tsconfig.app.json: tsconfig.server.json по умолчанию наследует его оттуда.
Свои реализации Zone.js и Noop пока не поддерживаются¶
Гидратация опирается на сигнал Zone.js о том, что приложение стало стабильным. По этому сигналу Angular на сервере начинает сериализацию, а на клиенте — уборку после гидратации: удаляет узлы DOM, которые так и не были сопоставлены.
Своя реализация Zone.js или реализация «noop» может сдвинуть момент события «stable», и сериализация или уборка начнутся слишком рано или слишком поздно. Эта конфигурация пока поддерживается не полностью: момент события onStable в своей реализации Zone.js, возможно, придётся подстроить.
Ошибки¶
Ошибки гидратации бывают разными: от несовпадения узлов до случая, когда ngSkipHydration стоит на недопустимом узле-хосте. Чаще всего ошибка из-за прямой работы с DOM через нативные API: на клиенте гидратация не находит дерево DOM, которое отрисовал сервер. Другой такой случай описан выше в разделе Корректная структура HTML. Следите, чтобы HTML в шаблонах был корректным, и этот случай не возникнет.
Полный список ошибок гидратации — в справочнике ошибок.
Как пропустить гидратацию отдельных компонентов¶
Отдельные компоненты с гидратацией работают плохо из-за проблем вроде прямой работы с DOM. Обходной путь — атрибут ngSkipHydration на теге компонента: гидратация всего компонента пропускается.
1 | |
Тот же ngSkipHydration можно задать привязкой хоста.
1 2 3 4 5 | |
Атрибут ngSkipHydration заставляет Angular пропустить гидратацию всего компонента и его потомков. Компонент ведёт себя так, будто гидратации нет: уничтожает себя и рисует заново.
Так чинятся проблемы отрисовки, но у этого компонента и его потомков не будет выгод гидратации. Чтобы убрать пропуск, реализацию нужно поправить и уйти от приёмов, которые ломают гидратацию (то есть от прямой работы с DOM).
Атрибут ngSkipHydration допустим только на узле-хосте компонента. На других узлах Angular выбросит ошибку.
Если поставить ngSkipHydration на корневой компонент приложения, гидратация фактически выключится для всего приложения. Атрибут — крайняя мера. Компоненты, которые ломают гидратацию, стоит считать ошибками и исправлять.
Момент гидратации и стабильность приложения¶
Стабильность приложения — важная часть гидратации. Гидратация и всё, что идёт после неё, начинаются только когда приложение сообщило о стабильности. Стабильность задерживают по-разному: таймауты и интервалы, неразрешённые промисы, незавершённые микрозадачи. Тогда возможна ошибка приложение остаётся нестабильным: за 10 секунд приложение так и не стало стабильным. Если гидратация не начинается сразу, посмотрите, что держит стабильность, и уберите эти задержки.
Отладка стабильности приложения¶
Утилита provideStabilityDebugging показывает, почему приложение не стабилизируется. В режиме разработки она уже есть, если используется provideClientHydration. Её можно добавить в провайдеры вручную — для продакшен-сборок или для SSR без гидратации. Если стабилизация занимает больше ожидаемого, утилита пишет сведения в консоль.
1 2 3 4 5 6 7 | |
Когда утилита включена, она пишет в консоль незавершённые задачи (PendingTasks). Если приложение на Zone.js, импорт zone.js/plugins/task-tracking показывает, какие макрозадачи не дают зоне Angular стабилизироваться. Плагин даёт стек создания макрозадачи и помогает найти источник задержки.
Angular не вырезает плагин отслеживания задач zone.js и эту утилиту из продакшен-сборок. Пользуйтесь ими только для временной отладки стабильности во время разработки, в том числе на оптимизированных продакшен-сборках.
Интернационализация¶
По умолчанию Angular пропускает гидратацию компонентов с блоками i18n и рисует такие компоненты заново.
Чтобы включить гидратацию блоков i18n, добавьте withI18nSupport в вызов provideClientHydration.
1 2 3 4 5 6 7 8 9 10 | |
Одинаковая отрисовка на сервере и на клиенте¶
Не ставьте блоки @if и другие условия, которые на сервере и на клиенте показывают разное содержимое, например @if с функцией Angular isPlatformBrowser. Разная отрисовка сдвигает макет, портит опыт и Core Web Vitals.
Сторонние библиотеки с работой с DOM¶
Ряд сторонних библиотек рисует себя через прямую работу с DOM. Наглядный пример — графики D3. Без гидратации они работали, а с ней могут давать ошибку несовпадения DOM. Пока что в таком случае ставьте атрибут ngSkipHydration на компонент, который рисуется этой библиотекой.
Сторонние скрипты с работой с DOM¶
Многие сторонние скрипты, например трекеры рекламы и аналитика, меняют DOM до гидратации. Страница перестаёт совпадать со структурой, которую ждёт Angular, и гидратация падает с ошибкой. Такие скрипты лучше откладывать до момента после гидратации. Чтобы отложить скрипт до процессов после гидратации, смотрите AfterNextRender.
Инкрементальная гидратация¶
Инкрементальная гидратация — расширенный вид гидратации: момент гидратации задаётся точнее. Подробнее — в руководстве по инкрементальной гидратации.
Источник: https://angular.dev/guide/hydration