Ключові особливості Next.js
В інтерв'ю це питання перевіряє, наскільки глибоко ви знаєте фреймворк, а не лише те, що ви про нього чули. Недостатньо просто перерахувати "SSR, маршрутизація, оптимізація". Вам потрібно зрозуміти, як працює кожна функція і чому вона існує.
Давайте розглянемо основні функції Next.js по черзі.
Гібридний рендеринг
Основна сила Next.js полягає в тому, що ви самі обираєте стратегію рендерингу для кожної сторінки. Різні сторінки в одному додатку можуть використовувати різні підходи:
- SSG статичний, генерується під час збірки. Для сторінок, які рідко змінюються.
- SSR рендериться на сервері при кожному запиті. Для динамічного контенту.
- ISR статичний з фоновою перевіркою. Компроміс між SSG і SSR.
- CSR рендеринг на стороні клієнта, як у звичайному додатку React.
// Сторінка "Про нас" на IT Lead — статична (SSG за замовчуванням)
export default async function AboutPage() {
const info = await fetch('https://api.itlead.org/about')
const data = await info.json()
return <About data={data} />
}// Потік проблем — оновлюється кожні 5 хвилин (ISR)
export default async function ProblemsPage() {
const res = await fetch('https://api.itlead.org/problems', {
next: { revalidate: 300 }
})
const problems = await res.json()
return <ProblemList problems={problems} />
}Порада для інтерв'ю:
Звичайне запитання: "Коли б ви обрали SSR замість SSG?". Гарна відповідь: SSR для сторінок з персоналізованим контентом (профіль, інформаційна панель), SSG для контенту, який однаковий для всіх (блог, документація).
App Router і маршрутизація на основі файлів
Починаючи з версії 13, Next.js використовує App Router. Структура папок всередині каталогу app безпосередньо визначає ваші маршрути:
-
app
-
layout.tsx
-
page.tsx
-
docs
-
page.tsx
-
[slug]
-
page.tsx
-
problems
-
page.tsx
-
loading.tsx
-
error.tsx
Кожна папка є сегментом URL. Спеціальні файли мають конкретні ролі:
| Файл | Призначення |
|---|---|
page.tsx | UI сторінки |
layout.tsx | Спільний обгортка для сторінки та вкладених маршрутів |
loading.tsx | UI завантаження (працює з Suspense) |
error.tsx | UI помилки (працює з Error Boundary) |
not-found.tsx | 404 UI |
// app/docs/[slug]/page.tsx
// Динамічний маршрут: /docs/javascript, /docs/react тощо.
export default async function DocPage({
params
}: {
params: { slug: string }
}) {
const doc = await getDocument(params.slug)
return <article>{doc.content}</article>
}Серверні компоненти
У App Router компоненти за замовчуванням є серверними компонентами. Це означає:
- Вони виконуються лише на сервері
- Їхній код не включається в пакет для клієнта
- Вони можуть безпосередньо отримувати доступ до бази даних, файлової системи, змінних середовища
// Серверний компонент — код не надсилається в браузер
import { db } from '@/lib/db'
export default async function UserStats() {
const totalUsers = await db.user.count()
const totalProblems = await db.problem.count()
return (
<div>
<p>Користувачі на IT Lead: {totalUsers}</p>
<p>Проблеми: {totalProblems}</p>
</div>
)
}Коли вам потрібна інтерактивність, позначте компонент як клієнтський компонент:
'use client'
import { useState } from 'react'
export default function ThemeToggle() {
const [dark, setDark] = useState(false)
return (
<button onClick={() => setDark(!dark)}>
{dark ? 'Світлий режим' : 'Темний режим'}
</button>
)
}Важливо:
Серверні компоненти не можуть використовувати useState, useEffect, onClick та інші клієнтські API. Якщо вам потрібна інтерактивність, виділіть її в окремий клієнтський компонент.
Серверні дії
Серверні дії дозволяють вам викликати серверні функції безпосередньо з компонентів без створення API-інтерфейсів:
// actions/subscribe.ts
'use server'
import { db } from '@/lib/db'
export async function subscribe(email: string) {
await db.subscriber.create({
data: { email }
})
}// Клієнтський компонент викликає серверну функцію
'use client'
import { subscribe } from '@/actions/subscribe'
export default function SubscribeForm() {
return (
<form action={async (formData) => {
const email = formData.get('email') as string
await subscribe(email)
}}>
<input name="email" type="email" placeholder="Email" />
<button type="submit">Підписатися на IT Lead</button>
</form>
)
}Вкладені макети
Макети в Next.js зберігають стан між навігаціями. Якщо у вас є бокова панель або навігація, вони не перерисовуються при переході між сторінками:
// app/docs/layout.tsx
// Цей макет обгортає всі /docs/* сторінки
export default function DocsLayout({
children
}: {
children: React.ReactNode
}) {
return (
<div className="flex">
<Sidebar />
<main className="flex-1">{children}</main>
</div>
)
}При переході з /docs/javascript на /docs/react макет з боковою панеллю залишається на місці. Лише вміст children перерисовується.
Вбудована оптимізація
Компонент зображення
Автоматично оптимізує зображення: змінює розмір, конвертує в WebP/AVIF, додає лінійне завантаження:
import Image from 'next/image'
export default function Avatar() {
return (
<Image
src="/avatar.png"
width={64}
height={64}
alt="Аватар користувача"
/>
)
}Компонент посилання
Попередньо завантажує сторінку у фоновому режимі, коли посилання входить у видимість. Навігація стає майже миттєвою:
import Link from 'next/link'
export default function Nav() {
return (
<nav>
<Link href="/problems">Проблеми</Link>
<Link href="/docs">Документація</Link>
</nav>
)
}Оптимізація шрифтів
next/font завантажує шрифти без зсуву макета та без запитів на стороні клієнта до Google Fonts:
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin', 'cyrillic'] })
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html className={inter.className}>
<body>{children}</body>
</html>
)
}Проміжне ПЗ
Проміжне ПЗ виконується перед обробкою запиту і дозволяє вам перенаправляти, переписувати URL або змінювати заголовки:
// middleware.ts (в корені проекту)
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const locale = request.cookies.get('locale')?.value || 'en'
if (!request.nextUrl.pathname.startsWith(`/${locale}`)) {
return NextResponse.redirect(
new URL(`/${locale}${request.nextUrl.pathname}`, request.url)
)
}
}
export const config = {
matcher: ['/((?!api|_next|favicon).*)']
}Вбудована підтримка метаданих
Next.js надає зручний API для управління SEO-метаданими:
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'JavaScript Problems — IT Lead',
description: 'Розв'язуйте проблеми з реальних фронтенд-інтерв'ю',
openGraph: {
title: 'JavaScript Problems',
description: 'Розв'язуйте проблеми з реальних фронтенд-інтерв'ю',
type: 'website'
}
}
export default function ProblemsPage() {
return <ProblemsList />
}Повна картина
Запит
Проміжне ПЗ
App Router
Макет
Серверний компонент
Клієнтський компонент
База даних
Зовнішній API
Серверна дія Як відповісти на інтерв'ю:
Не перераховуйте всі функції. Виберіть 3-4 ключові та поясніть, як вони пов'язані. Наприклад: "Next.js використовує App Router з маршрутизацією на основі файлів, де компоненти за замовчуванням є серверними компонентами, що зменшує пакет для клієнта та надає прямий доступ до даних без API-шару через серверні дії".
Корисні ресурси
- nextjs.org/docs — офіційна документація Next.js
- nextjs.org/learn — інтерактивний курс від Vercel
- nextjs.org/blog — блог з оголошеннями про випуски
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.