awesome-everything EN
↑ Обратно к восхождению

Браузер и фронтенд-рантайм

Стратегии рендеринга: чтение кода и конфигов

Суть Читайте реальные сниппеты React/Next — источник hydration mismatch, гонку SSR-фетча, конфиг ISR revalidate и расстановку Suspense — и выбирайте исправление с наибольшим рычагом.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min

Баги стратегий рендеринга читаются в коде и конфигах, а не в документации. Прочитайте каждый сниппет, предскажите его поведение в рантайме и выберите исправление, которое senior-инженер сделает первым.

Цель

Отработайте цикл, который проходит каждый инцидент со стратегией рендеринга: прочитать компонент или конфиг, предсказать поведение hydration, свежести или streaming и взяться за структурное исправление — а не за заплатку поверх симптома.

Сниппет 1 — мигающее приветствие

function Greeting() {
  // читается во время рендера, на сервере И на клиенте
  const hour = new Date().getHours();
  const msg = hour < 12 ? 'Good morning' : 'Good evening';
  return <h1>{msg}</h1>;
}
Викторина

Этот компонент выбрасывает 'Text content did not match' при hydration и поднимает CLS. Где баг и каково верное исправление?

Сниппет 2 — SSR-фетч данных

// Client Component, рендерится на сервере, затем гидрируется
function Profile({ userId }) {
  const [user, setUser] = useState(null);
  useEffect(() => {
    fetch(`/api/users/${userId}`).then(r => r.json()).then(setUser);
  }, [userId]);
  return <span>{user?.name ?? 'Loading…'}</span>;
}
Викторина

Страница рендерится на сервере, но этот компонент всегда печатает 'Loading…' в начальном HTML, а реальное имя появляется только после hydration. Почему и какой подход даёт больший рычаг?

Сниппет 3 — конфиг ISR

// Сегмент маршрута Next.js App Router
export const revalidate = 3600; // секунды

async function ProductPage({ params }) {
  const product = await getProduct(params.id); // кэшируется, ревалидируется раз в час
  return <ProductView product={product} />;
}
Викторина

Цены меняются непредсказуемо, иногда с интервалом в минуты, но `revalidate = 3600` означает, что цена может быть устаревшей до часа. Как сохранить выигрыш ISR в статической доставке, ограничив устаревание секундами?

Сниппет 4 — расстановка Suspense

async function Dashboard() {
  return (
    <Shell>
      <Stats />                       {/* быстро: ~10 мс */}
      <Suspense fallback={<Spinner/>}>
        <OrderHistory />              {/* медленно: запрос ~400 мс */}
      </Suspense>
    </Shell>
  );
}
Викторина

При streaming SSR (renderToPipeableStream) чего достигает такая расстановка Suspense и что было бы, если убрать границу?

Итог

Четыре паттерна чтения покрывают большинство инцидентов со стратегиями рендеринга: живое значение (new Date(), Math.random(), window), прочитанное во время рендера, — это hydration mismatch, отложите его в useEffect; useEffect никогда не выполняется во время SSR, поэтому данные надо фетчить на сервере и передавать вниз (или встраивать в dehydrated cache), чтобы они появились в начальном HTML; временной ISR ограничивает устаревание слабо, но on-demand ревалидация через вебхук сужает его до секунд, сохраняя доставку с CDN; а Suspense-граница вокруг медленного запроса — это то, что позволяет streaming SSR сбросить оболочку первой вместо блокировки на самой медленной части. Диагностируйте по коду и конфигу, исправляйте структуру, затем проверяйте TTFB, INP и число mismatch.

Продолжить восхождение ↑Стратегии рендеринга: per-route аудит рендеринга
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.