IT
📘

Полное руководство по Next.js 15 App Router: серверные компоненты и лучшие практики

USD/JPY分散は、為替急変局面で一方通貨の過大シェアを防ぎ、月次の再バランスと上限規則で感情的な一括投資を抑える実践設計です。

Полное руководство по Next.js 15 App Router: серверные компоненты и лучшие практики

Полное руководство по Next.js 15 App Router: серверные компоненты и лучшие практики

Краткое резюме: Next.js 15 App Router кардинально меняет подход к разработке React-приложений. Серверные компоненты (RSC) по умолчанию, улучшенное кэширование, PPR (Partial Prerendering) и встроенная поддержка React 19 делают Next.js 15 наиболее производительным фреймворком для современных веб-приложений.

Ключевые изменения в Next.js 15

large gray ship sitting next body water

Next.js 15 (октябрь 2024) принёс несколько критических изменений:

  1. 1params и searchParams теперь асинхронные — обязателен await params в page.tsx
  2. 2Кэширование по умолчанию отключено для fetch(), GET обработчиков и клиентских маршрутов
  3. 3React 19 RC — поддержка новых хуков и улучшений Concurrent Mode
  4. 4Turbopack теперь стабилен для dev-режима
  5. 5after() API — выполнение кода после ответа (аналитика, логирование)

💡 Реальный инсайт: По данным Vercel, сайты на Next.js 15 с правильно настроенным PPR показывают TTFB (Time to First Byte) в 200–400 мс против 800–1500 мс у традиционных SPA. Это напрямую влияет на Core Web Vitals и позиции в поиске.

Серверные компоненты: основы

fighter jet sitting on aircraft carrier

По умолчанию все компоненты в App Router являются серверными (React Server Components, RSC).

Что могут делать серверные компоненты:

  • Напрямую обращаться к базе данных
  • Читать файловую систему
  • Использовать серверные env-переменные
  • Выполнять асинхронные запросы без useEffect

Что НЕ могут:

  • Использовать хуки useState, useEffect, useContext
  • Обрабатывать события (onClick, onChange)
  • Обращаться к браузерным API (window, document)

Правильное разделение Client/Server:

typescript
// app/dashboard/page.tsx — серверный компонент
export default async function DashboardPage() {
  // Прямой запрос к БД — работает!
  const data = await db.query('SELECT * FROM posts LIMIT 10')

  return (
    <div>
      <h1>Dashboard</h1>
      {/* Передаём данные в клиентский компонент */}
      <InteractiveChart data={data} />
    </div>
  )
}

// components/InteractiveChart.tsx — клиентский компонент
'use client'
import { useState } from 'react'

export function InteractiveChart({ data }) {
  const [filter, setFilter] = useState('all')
  // ... интерактивная логика
}

Обязательный await params в Next.js 15

Критическое изменение: params и searchParams теперь Promise.

typescript
// ❌ Неправильно (Next.js 14 стиль)
export default function Page({ params }: { params: { slug: string } }) {
  const { slug } = params  // В Next.js 15 это объект Promise, не строка!
}

// ✅ Правильно (Next.js 15)
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params  // Обязательно await
}

Стратегии кэширования

В Next.js 15 кэширование больше не автоматическое — его нужно явно настраивать.

Варианты для fetch():

typescript
// Без кэширования (по умолчанию в Next.js 15)
fetch('https://api.example.com/data')

// Кэширование принудительно
fetch('https://api.example.com/data', { cache: 'force-cache' })

// ISR — перевалидация каждые 60 секунд
fetch('https://api.example.com/data', { next: { revalidate: 60 } })

// Тэговое кэширование
fetch('https://api.example.com/data', { next: { tags: ['posts'] } })

Ревалидация по тэгу:

typescript
// app/api/revalidate/route.ts
import { revalidateTag } from 'next/cache'

export async function POST(request: Request) {
  const { tag } = await request.json()
  revalidateTag(tag)  // Инвалидирует все fetch с этим тэгом
  return Response.json({ revalidated: true })
}

Partial Prerendering (PPR)

PPR — революционный подход: статический shell отрисовывается мгновенно, динамические части загружаются через Suspense.

typescript
// next.config.ts
export default {
  experimental: { ppr: true }
}

// app/product/[id]/page.tsx
import { Suspense } from 'react'

export default async function ProductPage({ params }) {
  const { id } = await params
  return (
    <div>
      {/* Статический контент — отрисовывается немедленно */}
      <StaticHeader />

      {/* Динамический контент — загружается параллельно */}
      <Suspense fallback={<PriceSkeleton />}>
        <DynamicPrice productId={id} />
      </Suspense>

      <Suspense fallback={<ReviewsSkeleton />}>
        <DynamicReviews productId={id} />
      </Suspense>
    </div>
  )
}

Параллельные и перехватывающие маршруты

Параллельные маршруты (@folder):

app/
  layout.tsx
  @dashboard/
    page.tsx    ← /dashboard
  @analytics/
    page.tsx    ← /analytics
  page.tsx
typescript
// app/layout.tsx
export default function Layout({
  dashboard,
  analytics,
}: {
  dashboard: React.ReactNode
  analytics: React.ReactNode
}) {
  return (
    <div className="grid grid-cols-2">
      {dashboard}
      {analytics}
    </div>
  )
}

Оптимизация производительности

1. Оптимизация изображений:

typescript
import Image from 'next/image'

// ✅ Всегда указывайте width/height для предотвращения CLS
<Image
  src="/hero.jpg"
  alt="Главное изображение"
  width={1200}
  height={628}
  priority  // для LCP-изображений выше fold
  sizes="(max-width: 768px) 100vw, 50vw"
/>

2. Шрифты с next/font:

typescript
import { Inter } from 'next/font/google'

const inter = Inter({
  subsets: ['latin', 'cyrillic'],
  display: 'swap',  // Предотвращает невидимый текст при загрузке
})

3. Динамический импорт:

typescript
import dynamic from 'next/dynamic'

// Тяжёлые компоненты загружаются только когда нужны
const HeavyChart = dynamic(() => import('./HeavyChart'), {
  loading: () => <ChartSkeleton />,
  ssr: false  // Только клиентская отрисовка
})

FAQ

Q1. В чём разница между App Router и Pages Router? A: App Router (Next.js 13+) использует серверные компоненты по умолчанию, поддерживает вложенные layouts, параллельные маршруты и streaming. Pages Router — предыдущее поколение с getServerSideProps/getStaticProps.

Q2. Когда использовать 'use client'? A: Только когда компоненту нужны: useState, useEffect, обработчики событий, доступ к browser API. Минимизируйте клиентские компоненты для лучшей производительности.

Q3. Как обрабатывать аутентификацию в App Router? A: Используйте middleware.ts для перенаправления неаутентифицированных пользователей, или проверяйте сессию в layout.tsx/page.tsx серверных компонентах.

Q4. Почему мои данные не обновляются после мутации? A: В Next.js 15 вызовите revalidatePath('/path') или revalidateTag('tag') в Server Action или API route после изменения данных.

Q5. Как настроить middleware для i18n? A: В middleware.ts читайте Accept-Language заголовок и перенаправляйте на /[lang]/... маршруты. Используйте next/headers для чтения куки с предпочтительным языком.

Q6. Совместим ли Next.js 15 с Cloudflare Pages? A: Да, при использовании @opennextjs/cloudflare. Не используйте export const runtime = 'edge' — OpenNext обрабатывает это автоматически.

🔧 Related Free Tools

Похожее