Суть Читаем четыре реальных сниппета фронтенда — размещение глобального состояния, fetch-waterfall, ленивый импорт, ссылку на токен — и выбираем фикс, который senior делает первым.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min
Архитектура читается в коде, а не в слайдах. Каждый сниппет ниже — это слой трека, пойманный в момент, когда он идёт не так. Прочитайте код, спрогнозируйте цену и выберите фикс, бьющий по слою, которому реально принадлежит баг.
Цель
Отработать кросс-трековый цикл диагностики: прочитать сниппет, определить, какому слою он принадлежит (state, fetch, splitting, токены), и выбрать самый рычажный фикс вместо того, что патчит симптом выше.
Ввод в поле поиска тормозит весь дашборд. По этому коду — в чём причина и фикс?
Heads-up Store с селекторами ре-рендерят только подписчиков, чей выбранный срез изменился. Тормоза реальны, потому что Chart/Table/Sidebar подписаны на состояние, делящее уведомления об обновлениях с `search` — лечение в том, чтобы вообще не класть per-keystroke состояние в общий store.
Heads-up memo защищает от изменения пропсов, но эти компоненты ре-рендерятся, потому что подписаны на store, обновляющийся на каждое нажатие. Вы обернёте memo всё дерево и всё равно заплатите; фикс — перенести состояние, а не мемоизировать каждого потребителя.
Heads-up Дебаунс снижает частоту, но оставляет нажатие в глобальном состоянии, поэтому ввод всё равно ощущается лагающим, а архитектура всё ещё неверна. Колокация убирает радиус поражения целиком; дебаунс — это патч на неправильно размещённом куске состояния.
Сниппет 2 — граф fetch
function ProductPage({ id }) { const product = useQuery(["product", id], () => fetchProduct(id)); // эти два НЕ зависят от product, но ждут его: const reviews = useQuery(["reviews", id], () => fetchReviews(id), { enabled: !!product.data, }); const related = useQuery(["related", id], () => fetchRelated(id), { enabled: !!product.data, }); // ...рендерит после резолва всех трёх}
Викторина
Completed
Отзывы и похожие товары не зависят от полезной нагрузки product, и всё же LCP медленный. В чём дефект и фикс?
Heads-up Флаги enabled — ровно этот баг: они создают зависимость, которой нет в данных. reviews и related нужен лишь `id`, доступный немедленно, поэтому гейтинг на product.data добавляет два лишних последовательных round-trip'а.
Heads-up Кэш помогает повторным визитам, но ничего не делает для первой отрисовки, где waterfall и бьёт по LCP. Структурный фикс — распараллелить независимые fetch'и, а не полагаться на тёплый кэш.
Heads-up Схлопывание хуков косметично; если этот единственный запрос всё равно ждёт product перед reviews/related, waterfall выживает. Убрать ложную зависимость — а не слить хуки — это то, что распараллеливает round-trip'ы.
Библиотека графиков на 400 КБ уходит в начальный бандл, хотя большинство пользователей никогда не жмут 'Show charts'. Почему и в чём фикс?
Heads-up Условный рендер контролирует, когда компонент монтируется, а не попадает ли его модуль в бандл. Статический импорт вычисляется на этапе сборки, поэтому 400 КБ уходят вперёд — только динамический import() выделяет их в отдельный чанк.
Heads-up Tree-shaking убирает доказуемо неиспользуемые экспорты, но HeavyChart используется (он рендерится при open), поэтому вытряхнуть его нельзя. Отсрочка его загрузки требует code-splitting через динамический импорт, а не tree-shaking.
Heads-up Отдельный пакет всё равно статически импортируется в этот чанк тем же образом; граница пакета не разбивает бандл. Рантайм code-splitting через lazy()/import() — это то, что держит его вне начальной загрузки.
Тёмной теме нужно, чтобы `.btn-danger` читался иначе на тёмной поверхности, а ребрендинг сменит красный. Что вскрывает этот CSS и в чём структурный фикс?
Heads-up Покомпонентные dark-оверрайды воссоздают проблему хардкода во втором месте и масштабируются линейно с числом компонентов. Семантический токен, резолвимый по теме, означает, что компонент объявляет намерение один раз, а тему поставляет значение.
Heads-up Sass-переменная этапа сборки не может переключаться в рантайме под тёмную тему так, как CSS custom property, и всё равно привязывает компонент к примитиву, а не к семантической роли вроде surface-danger. Фикс — рантайм-слой семантических токенов.
Heads-up Использование одного hex везде — ровно эта ловушка: нет косвенности, поэтому ребрендинг — это find-and-replace, а у тёмной темы нет переключателя. Согласованность значения не делает его токеном; токен несёт смысл, который тема может перемаппить.
Итог
Четыре сниппета, четыре слоя, одна привычка: per-keystroke состояние должно быть колокейтнуто, а не в глобальном store; гейт enabled на независимом fetch’е — это искусственный waterfall; статический импорт бандлит жадно, а только динамический import() разбивает; захардкоженный hex — это отсутствующий семантический токен. В каждом случае самый рычажный фикс живёт на слое, которому принадлежит баг — перенести состояние, убрать ложную зависимость, разбить чанк, добавить семантический слой — никогда не патч на слой выше.