Пост 2. Магия и Монстр: Гидратация и «Расхождения» 💧👾
Введение Сервер отдал идеальный HTML. Браузер его отрисовал. Пользователь доволен. Но потом Vue делает свою магию, и в консоли вылетает жирное красное: Hydration completed but contains mismatches. Что сломалось?
🧩 Аналогия (я не осилил ресторан, будет театр):
В ролях: декораторы (сервер), зритель (пользователь приложения), актер (Vue на стороне клиента), сценарий (виртуальный DOM)
Декораторы построили декорации, расставили мебель, повесили шторы, расстелили ковры. Зрители уже сидят в зале и видят красивую, но статичную сцену. Актеры выходят и сцена оживает. Они смотрят на сценарий, в котором строго описано, что они должны делать
По сценарию, актер должен торжественно сесть в кресло, но декораторы ошиблись и поставили на его место табуретку. Актер совсем не умеет в импровизацию, он просто закатывает истерику, сбрасывает все декорации и начинает самостоятельно собирать весь декор заново. Зритель явно недоволен таким представлением, ведь вместо представления у него вспышки ярости актера.
В браузере это выглядит примерно так, пока Vue пересчитывает все на клиенте - пользователь смотрит на мерцание страницы.
⚙️ Техническая суть Гидратация — это процесс, когда клиентский JS скачивает Vue, строит виртуальный DOM и сравнивает его с реальным DOM, который пришел от сервера. Если они совпадают — Vue просто навешивает обработчики событий (@click и тд). Если нет — происходит «расхождение» (mismatch). При расхождении Vue на стороне клиента начнет перестраивать DOM так, как он его видит. А пользователь в это время увидит мерцание. Вся прелесть ускорения от SSR аннигилируется
Главные виновники: 1. Недетерминированные значения: Math.random(), new Date() - тут просто будут разные значения на сервере и на клиенте 2. Невалидный HTML: например, <p><div>Текст</div></p>. Браузер сам «починит» этот HTML при парсинге, изменив структуру, и Vue на клиенте увидит другую DOM-дерево. 3. Сторонние либы, которые лезут в DOM при импорте.
💡 Советы по борьбе с монстром: 1. Никакой рандомности в шаблонах: Для уникальных id используйте новый useId() из Vue 3.4+. Он генерирует одинаковые ID на сервере и клиенте. 2. Валидируйте HTML: Поставьте @nuxtjs/html-validator. Он отловит вложенные <div> внутри <p> еще до деплоя. 3. Прячьте «браузерное»: Если компонент использует тяжелые клиентские либы (карты, графики), оборачивайте его в <ClientOnly>.
Заключение Гидратация — это мост между сервером и клиентом. Но этот мост очень хрупкий. Главное правило SSR: вывод сервера и клиента должен быть строго детерминированным. Никаких сюрпризов.
Рассказывайте свои истории увлекательных расхождений, будет интересно почитать)