Архитектура фронтенда
Data fetching: схлопни critical path
Читать про waterfall’ы — не то же самое, что вытащить реальный LCP с 2.9s до менее 1s. Построй страницу, которая фетчит всё на клиенте в waterfall, затем применяй лестницу юнита — перенеси данные LCP на сервер, распараллель остальное, застримь медленные части, закэшируй интерактивные — и доказывай каждый шаг числами из панели Network и Lighthouse.
Преврати ментальную модель юнита в воспроизводимый инженерный цикл: измерь critical path, определи, какие round-trip’ы предшествуют LCP, перемести и распараллель fetch’и, которые его запирают, застримь медленный хвост и подтверди выигрыш LCP и TTFB измерениями до/после при одинаковом троттлинге.
Возьми страницу товара/профиля, которая клиентски фетчит свой LCP-элемент через waterfall, и доведи LCP до менее 1.5s, а TTFB — до менее 200ms, не меняя API и данные — перемещая, распараллеливая и стримя fetch'и, доказывая каждый шаг измерениями.
- Таблица до/после: LCP, TTFB, число round-trip'ов critical path и размер клиентского бандла — всё измерено под одним пресетом троттлинга, не оценено.
- Панель Network после фикса показывает LCP-контент в исходном HTML/стриме и отсутствие последовательной лестницы среди независимых fetch'ей.
- LCP менее 1.5s и TTFB менее 200ms под троттлинг-профилем, а медленная не-LCP секция стримится за Suspense-fallback, а не блокирует первую отрисовку.
- Абзац-описание с указанием для каждого fetch'а, где он теперь выполняется (сервер / стрим / клиентский кэш) и почему — привязанное к тому, запирает ли он LCP.
- Добавь hover/viewport-prefetch для самой вероятной следующей навигации и измерь воспринимаемо-мгновенный переход; отметь компромисс с мобильным трафиком и где ты решил не префетчить.
- Добавь оптимистичную мутацию (like / bookmark / add-to-cart) с полным паттерном onMutate-снимок + onError-откат + onSettled-инвалидация и форсируй отказ сервера, чтобы доказать, что откат восстанавливает реальное прошлое состояние.
- Добавь поле search-as-you-type и намеренно воспроизведи гонку не по порядку, затем почини её AbortController'ом (или отменой по queryKey) и покажи, что последний запрос всегда побеждает.
- Сделай скоординированную многослойную инвалидацию для мутации: revalidateTag на сервере плюс queryClient.invalidateQueries на клиенте — и продемонстрируй, что пропуск клиентского шага оставляет браузер устаревшим, хотя сервер уже корректен.
Это цикл, который ты запустишь на каждом реальном LCP-инциденте: сначала измерь critical path, отдели round-trip’ы, запирающие LCP, от тех, что не запирают, перенеси данные LCP-элемента на сервер, распараллель и застримь остальное и держи на клиентском кэше только реально интерактивное состояние без рефетча того, что у сервера уже есть. Затем подтверди числами до/после под идентичным троттлингом. Сделав это раз на игрушечной странице, ты превращаешь продакшен-версию — где тот же waterfall стоит реальной выручки — в мышечную память.