Skip to content
maksim zaytsev
Заметки

15 января 2024 · 8 мин

Заметки об app router в Next.js 15

Я перенёс панель среднего размера с pages router на app router в Next.js 15 — где-то за три недели, попутно с обычной работой над фичами. Вот заметки, которые хотел бы иметь под рукой до того, как начал.

Что изменилось

Главный сдвиг — в голове: роутинг теперь это дерево папок, и у каждой папки может быть свой layout, свой loading-стейт и свой error boundary. getServerSideProps и getStaticProps исчезли — данные загружаются прямо в серверных компонентах, обычными async-функциями. Route handlers заменили API routes: один route.ts на сегмент вместо одного файла со switch по req.method.

Кеширование тоже изменило форму. Фетчи кешируются по умолчанию, если явно не отключить это — удобно, пока не забудешь, что это происходит, и не потратишь двадцать минут, разглядывая устаревшие данные. Теперь я по умолчанию ставлю новым фетчам { cache: 'no-store' } и включаю кеш осознанно, а не наоборот.

async function getInvoices(orgId: string) {
  const res = await fetch(`${API}/invoices?org=${orgId}`, {
    cache: 'no-store',
  });
  if (!res.ok) throw new Error('Не удалось загрузить счета');
  return res.json();
}

Что удивило

Серверные компоненты сочетаются с клиентскими проще, чем я ожидал: данные, загруженные на сервере, спускаются вниз пропсами, и только интерактивный лист дерева становится 'use client'. Удивило не так приятно другое — как легко случайная директива 'use client' в общем файле-утилите утягивает половину дерева в клиентский бандл. Я нашёл три таких случая почти случайно, просматривая bundle analyzer.

useSearchParams тоже принудительно переводит компонент в клиентский рендеринг целиком — это легко упустить, когда переписываешь страницу, которая раньше бесплатно читала router.query.

Что сделал бы иначе

Я бы сначала на бумаге провёл границу между сервером и клиентом, а не постфактум. Ещё завёл бы route groups с первого дня, а не добавлял их задним числом — переструктурировать папки, когда роуты уже стали боевыми URL, куда нервознее, чем должно быть. И написал бы loading.tsx заранее, а не как довесок: именно они меняют ощущение от всей миграции, потому что приложение перестаёт выглядеть сломанным, пока данные стримятся.

Итог: изменение хорошее, но app router вознаграждает тех, кто решил форму серверно-клиентского разделения до начала работы, а не посреди миграции.