Скачать CV

Debugging

Гидрация, которая ломается на локали, не на вёрстке

Сервер взял русский из URL. Первый клиентский проход прочитал navigator.language. Vue выбросил дерево. Страница работала, баг прятался до прыжка шапки.

На /ru/notes переключатель языка на один кадр показал немецкий. Vue выбросил поддерево шапки. Предупреждение о гидрации значит, что серверный HTML и первый клиентский рендер расходятся. Vue бросает это поддерево и собирает заново. Страница заметок всё ещё отвечала на клики, поэтому я забил на warning, пока хром не прыгнул.

Серверный entry берёт локаль из пути. Клиентский entry на первом кадре ещё падал на navigator.language. Chrome на этой машине de-DE. Половина строк оболочки сменилась. Даты сменились. Тело статьи пришло из API как ru, разошлась только оболочка. Этого хватает. Пользователи на русском URL видели немецкую шапку, пока гидрация не закончилась.

Расхождение было не в недостающем div. На одном запросе было два источника локали.

Два дерева расходятся. Vue выбрасывает серверную разметку этого поддерева.
Два дерева расходятся. Vue выбрасывает серверную разметку этого поддерева.

Одна строка локали

Источник это префикс URL. nginx уже уводит неизвестные префиксы. Node и Laravel получают en, ru или de. Это значение я кладу в состояние страницы, клиент читает его оттуда до первого рендера. navigator.language можно после mount, как подсказку, не как дерево.

То же правило для таймзоны и темы. Тема в localStorage серверу неизвестна. Рендерьте дефолт с обеих сторон, переключайте после mount. Вспышка лучше выброшенного дерева, где клик сел не на тот узел.

На клиенте я ещё отдавал Accept-Language в плагин i18n, а сервер этот заголовок игнорировал. Два bootstrap, два ответа. Локаль API из URL победила для тела. Плагин победил для оболочки. Одна строка в payload закрыла оба.

Фиксируйте локаль на краю. en-US в en до того, как её увидят Node и PHP.
Фиксируйте локаль на краю. en-US в en до того, как её увидят Node и PHP.

Как ловить

Warning гидрации в процессе Vue на разработке это ошибка. Гоняю одну деталку заметок, один список, одну админ-форму. Читаю только первый mismatch. Остальные обычно дети этого узла.

Битый HTML даёт тот же класс бага. div внутри p в сиде правит парсер браузера. Клиентское дерево никогда не совпадёт с серверной строкой. Чинить надо HTML статьи, не i18n. Это я увидел на одной длинной заметке, когда локаль уже была зафиксирована. Warning выглядел так же. Стек указывал на абзац, не на переключатель.

Гидрацию я не выключаю, чтобы спрятать это. SSR это смысл фронтенда. Если два прохода не сходятся на локали, они не сойдутся ни на чём, что от неё зависит. Production-сборка, которая глотает warning, всё равно везёт вспышку. Тест это русский URL в немецком браузере, первый кадр, язык шапки.

Какой опыт из этого

Смотрю первый mismatch гидрации и первый кадр шапки, не то, отвечают ли клики. Страница, которая отвечает на клики, всё ещё могла выбросить дерево.

navigator.language и Accept-Language до mount я не доверяю. Локаль это префикс URL. Тема и таймзона ждут после гидрации.

Правило, которое оставляю: одна строка локали в состоянии страницы, чтение до первого клиентского рендера. Если warning на абзаце, смотреть HTML статьи. Гидрацию не выключать.

К заметкам