Гидратация
Что такое гидратация
Гидратация — это процесс восстановления на клиенте приложения, отрисованного на стороне сервера. Он включает в себя повторное использование структур DOM, отрисованных сервером, сохранение состояния приложения, передачу данных приложения, которые уже были получены сервером, и другие процессы.
Почему гидратация важна?
Гидратация повышает производительность приложения, позволяя избежать лишней работы по повторному созданию узлов DOM. Вместо этого Angular во время выполнения пытается сопоставить существующие элементы DOM со структурой приложения и по возможности повторно использовать узлы DOM. Это повышает производительность, что можно измерить с помощью показателей основных интернет-показателей (CWV), например сокращения задержки при первом вводе (FID) и отрисовки самого крупного контента (LCP), а также совокупного сдвига макета (CLS). Улучшение этих показателей влияет и на такие параметры, как SEO.
Если гидратация не включена, приложения Angular, отрисованные на стороне сервера, удаляют и повторно отрисовывают DOM приложения, что может вызвать заметное мерцание пользовательского интерфейса. Такая повторная отрисовка может негативно повлиять на основные интернет-показатели, например LCP, и привести к сдвигу макета. Включение гидратации позволяет повторно использовать существующий DOM и предотвращает мерцание.
Как включить гидратацию в Angular
Гидратацию можно включить только для приложений с отрисовкой на стороне сервера (SSR). Сначала следуйте руководству по SSR в Angular, чтобы включить отрисовку на стороне сервера.
Использование Angular CLI
Если вы использовали Angular CLI для включения SSR (при создании приложения или позже с помощью ng add @angular/ssr), код для включения гидратации уже должен быть добавлен в приложение.
Настройка вручную
Если у вас нестандартная конфигурация и вы не использовали Angular CLI для включения SSR, гидратацию можно включить вручную: откройте главный компонент или модуль приложения и импортируйте provideClientHydration из @angular/platform-browser. Затем добавьте этот провайдер в список провайдеров для запуска приложения.
import {
bootstrapApplication,
provideClientHydration,
} from '@angular/platform-browser';
...
bootstrapApplication(App, {
providers: [provideClientHydration()]
});
Если вы используете NgModules, можно также добавить provideClientHydration в список провайдеров корневого модуля приложения.
import {provideClientHydration} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
@NgModule({
declarations: [App],
exports: [App],
bootstrap: [App],
providers: [provideClientHydration()],
})
export class AppModule {}
ВАЖНО: Убедитесь, что вызов provideClientHydration() также включен в набор провайдеров, используемый для запуска приложения на сервере. В приложениях со стандартной структурой проекта (созданных командой ng new) достаточно добавить вызов в корневой AppModule, так как этот модуль импортируется серверным модулем. Если у вас нестандартная конфигурация, добавьте вызов provideClientHydration() в список провайдеров в конфигурации запуска сервера.
Проверка включения гидратации
После настройки гидратации и запуска сервера откройте приложение в браузере.
ПОЛЕЗНО: Вероятно, перед тем как гидратация заработает в полной мере, потребуется устранить случаи прямого манипулирования DOM: заменить их средствами Angular или использовать ngSkipHydration. Подробнее см. разделы Ограничения, Прямое манипулирование DOM и Как пропустить гидратацию для отдельных компонентов.
Чтобы проверить, включена ли гидратация, запустите приложение в режиме разработки, откройте инструменты разработчика в браузере и просмотрите консоль. Вы должны увидеть сообщение со статистикой гидратации, например количеством гидратированных компонентов и узлов. Angular рассчитывает статистику для всех компонентов, отрисованных на странице, в том числе из сторонних библиотек.
Вы также можете использовать расширение Angular DevTools для браузера, чтобы увидеть состояние гидратации компонентов на странице. Angular DevTools также позволяет включить наложение, показывающее, какие части страницы были гидратированы. Если произошла ошибка несоответствия при гидратации, DevTools также выделит компонент, вызвавший ошибку.
Перехват и повторное воспроизведение событий
Когда приложение отрисовывается на сервере, оно становится видимым в браузере сразу после загрузки HTML. Пользователи могут предположить, что уже могут взаимодействовать со страницей, однако обработчики событий подключаются только после завершения гидратации. Начиная с версии 18 можно включить функцию повторного воспроизведения событий, которая перехватывает все события, произошедшие до гидратации, и воспроизводит их после её завершения. Включить эту функцию можно с помощью функции withEventReplay(), например:
import {provideClientHydration, withEventReplay} from '@angular/platform-browser';
bootstrapApplication(App, {
providers: [provideClientHydration(withEventReplay())],
}); Как работает повторное воспроизведение событий
Повторное воспроизведение событий улучшает взаимодействие с пользователем, перехватывая события, вызванные пользователем до завершения процесса гидратации. Затем эти события воспроизводятся, гарантируя, что ни одно действие пользователя не будет потеряно.
Повторное воспроизведение событий состоит из трех основных этапов:
-
Перехват действий пользователя
До завершения гидратации функция повторного воспроизведения событий перехватывает и сохраняет все действия пользователя, например щелчки и другие встроенные события браузера. -
Сохранение событий
Контракт событий хранит в памяти все действия, записанные на предыдущем этапе, чтобы впоследствии их можно было воспроизвести. -
Повторное воспроизведение событий
После завершения гидратации Angular повторно вызывает перехваченные события.
Повторное воспроизведение событий поддерживает встроенные события браузера, например click, mouseover и focusin. Подробнее о JSAction — библиотеке, обеспечивающей повторное воспроизведение событий, — можно узнать в файле readme.
Эта функция обеспечивает единообразное взаимодействие с пользователем и предотвращает игнорирование действий, выполненных до гидратации.
ПРИМЕЧАНИЕ: Если включена инкрементальная гидратация, повторное воспроизведение событий автоматически включается в фоновом режиме.
Ограничения
Гидратация накладывает на приложение несколько ограничений, которых нет, если она отключена. Структура DOM, создаваемая приложением, должна быть одинаковой на сервере и клиенте. Для гидратации требуется, чтобы дерево DOM в обоих случаях имело одинаковую структуру. Это также касается пробелов и узлов комментариев, которые Angular создает при отрисовке на сервере. Эти пробелы и узлы должны присутствовать в HTML, созданном в процессе серверной отрисовки.
ВАЖНО: HTML, созданный при серверной отрисовке, нельзя изменять между сервером и клиентом.
Если структура дерева DOM на сервере и клиенте различается, при гидратации возникнут проблемы с сопоставлением ожидаемых элементов с теми, которые фактически присутствуют в DOM. Чаще всего это происходит в компонентах, которые напрямую манипулируют DOM с помощью собственных API DOM.
Прямое манипулирование DOM
Если компоненты манипулируют DOM с помощью собственных API DOM или используют innerHTML или outerHTML, при гидратации возникнут ошибки. К проблемному манипулированию DOM относятся, например, обращение к document, поиск определенных элементов и добавление узлов с помощью appendChild. Отсоединение узлов DOM и их перемещение в другие места также приведут к ошибкам.
Это происходит потому, что Angular не знает об этих изменениях DOM и не может обработать их во время гидратации. Angular ожидает определенную структуру, но при попытке гидратации обнаруживает другую. Такое несоответствие приведет к сбою гидратации и вызовет ошибку несоответствия 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 пока не поддерживаются
Гидратация использует сигнал от Zone.js о том, что приложение перешло в стабильное состояние. Это позволяет Angular начать процесс сериализации на сервере или выполнить очистку после гидратации на клиенте, удалив узлы DOM, которые остались невостребованными.
Пользовательская реализация Zone.js или реализация «noop» может изменить момент возникновения события «стабильности», из-за чего сериализация или очистка начнется слишком рано или слишком поздно. Такая конфигурация пока не поддерживается полностью, и может потребоваться изменить время возникновения события onStable в пользовательской реализации Zone.js.
Ошибки
Вы можете столкнуться с различными ошибками, связанными с гидратацией: от несоответствия узлов до случаев, когда ngSkipHydration используется для недопустимого узла-хоста. Наиболее распространенная ошибка возникает из-за прямого манипулирования DOM с помощью собственных API, в результате чего при гидратации не удается найти или сопоставить на клиенте ожидаемую структуру дерева DOM, отрисованную сервером. Еще один случай, когда может возникнуть такая ошибка, описан выше в разделе Корректная структура HTML. Поэтому убедитесь, что HTML в шаблонах имеет корректную структуру, чтобы избежать этой ошибки.
Полный список ошибок, связанных с гидратацией, см. в справочнике по ошибкам.
Как пропустить гидратацию для отдельных компонентов
Некоторые компоненты могут работать некорректно при включенной гидратации из-за упомянутых выше проблем, например прямого манипулирования DOM. В качестве обходного решения добавьте атрибут ngSkipHydration к тегу компонента, чтобы пропустить гидратацию всего компонента.
<app-example ngSkipHydration />
В качестве альтернативы можно задать ngSkipHydration в качестве привязки хоста.
@Component({
...
host: {ngSkipHydration: 'true'},
})
class ExampleComponent {}
Атрибут ngSkipHydration заставляет Angular пропустить гидратацию всего компонента и его дочерних элементов. При использовании этого атрибута компонент ведет себя так, как если бы гидратация была отключена: он удаляет себя и выполняет повторную отрисовку.
ПОЛЕЗНО: Это устранит проблемы с отрисовкой, но означает, что для этого компонента и его дочерних элементов преимущества гидратации будут недоступны. Чтобы удалить аннотацию пропуска гидратации, необходимо изменить реализацию компонента и устранить несовместимые с гидратацией шаблоны поведения (например, прямое манипулирование DOM).
Атрибут ngSkipHydration можно использовать только для узлов-хостов компонентов. Если добавить этот атрибут к другим узлам, Angular выдаст ошибку.
Помните, что добавление атрибута ngSkipHydration к корневому компоненту приложения фактически отключит гидратацию во всем приложении. Используйте этот атрибут осторожно и обдуманно. Он предназначен для использования в крайнем случае. Компоненты, нарушающие работу гидратации, следует считать содержащими ошибки, которые необходимо исправить.
Время гидратации и стабильность приложения
Стабильность приложения — важная часть процесса гидратации. Гидратация и любые процессы, выполняемые после нее, начинаются только после того, как приложение сообщит о переходе в стабильное состояние. Стабилизацию могут задержать разные факторы, например таймеры и интервалы, неразрешенные промисы и ожидающие микрозадачи. В таких случаях может возникнуть ошибка «Приложение остается нестабильным», которая означает, что приложение не перешло в стабильное состояние за 10 секунд. Если гидратация приложения начинается не сразу, выясните, что мешает ему стабилизироваться, и устраните причины задержек.
Отладка стабильности приложения
Средство provideStabilityDebugging помогает выяснить, почему приложение не переходит в стабильное состояние. По умолчанию это средство предоставляется в режиме разработки при использовании provideClientHydration. Его также можно вручную добавить к провайдерам приложения, например для использования в производственных сборках или при использовании SSR без гидратации. Если стабилизация приложения занимает больше ожидаемого времени, средство выводит сведения в консоль.
import {provideStabilityDebugging} from '@angular/core';
import {bootstrapApplication} from '@angular/platform-browser';
import 'zone.js/plugins/task-tracking'; // Use if you have Zone.js with `provideZoneChangeDetection`
bootstrapApplication(App, {
providers: [provideStabilityDebugging()],
});
Если средство включено, оно выводит в консоль ожидающие задачи (PendingTasks). Если приложение использует Zone.js, можно также импортировать zone.js/plugins/task-tracking, чтобы узнать, какие макрозадачи мешают стабилизации зоны Angular. Этот плагин предоставляет трассировку стека создания макрозадачи, помогая определить источник задержки.
ВАЖНО: Angular не удаляет плагин отслеживания задач zone.js или это средство из производственных сборок. Используйте их только для временной отладки проблем со стабильностью во время разработки, в том числе для оптимизированных производственных сборок.
Интернационализация
ПОЛЕЗНО: По умолчанию Angular пропускает гидратацию компонентов, использующих блоки i18n, и полностью отрисовывает эти компоненты заново.
Чтобы включить гидратацию для блоков i18n, добавьте withI18nSupport к вызову provideClientHydration.
import {
bootstrapApplication,
provideClientHydration,
withI18nSupport,
} from '@angular/platform-browser';
...
bootstrapApplication(App, {
providers: [provideClientHydration(withI18nSupport())]
}); Единообразная отрисовка на стороне сервера и клиента
Не добавляйте блоки @if и другие условные конструкции, которые отображают разное содержимое при отрисовке на стороне сервера и клиента, например блок @if с функцией isPlatformBrowser из Angular. Такие различия при отрисовке приводят к сдвигам макета, ухудшая взаимодействие с пользователем и основные интернет-показатели.
Сторонние библиотеки, манипулирующие DOM
Некоторые сторонние библиотеки используют манипулирование DOM для отрисовки. Яркий пример — диаграммы D3. Такие библиотеки работают без гидратации, но при ее включении могут вызывать ошибки несоответствия DOM. Пока что, если при использовании одной из этих библиотек возникает ошибка несоответствия DOM, добавьте атрибут ngSkipHydration к компоненту, который выполняет отрисовку с помощью этой библиотеки.
Сторонние скрипты, манипулирующие DOM
Многие сторонние скрипты, например средства отслеживания рекламы и аналитики, изменяют DOM до начала гидратации. Эти скрипты могут вызывать ошибки гидратации, поскольку структура страницы перестает соответствовать ожидаемой Angular. По возможности откладывайте выполнение таких скриптов до завершения гидратации. Чтобы задержать запуск скрипта до завершения процессов после гидратации, используйте AfterNextRender.
Инкрементальная гидратация
Инкрементальная гидратация — это расширенный вариант гидратации, позволяющий точнее управлять тем, когда она происходит. Подробнее см. в руководстве по инкрементальной гидратации.
Super-powered by Google ©2010–2025.
Code licensed under an MIT-style License. Documentation licensed under CC BY 4.0.
https://angular.dev/guide/hydration