Cache Components у Next.js 16: use cache, PPR та питання для співбесід у 2026 році

Повний посібник з Cache Components у Next.js 16: директива use cache, Partial Pre-Rendering, cacheLife, cacheTag, безпека з use cache private та питання для senior-розробників на співбесідах.

Cache Components у Next.js 16 - use cache, PPR та питання для співбесід

Cache Components у Next.js 16 являють собою найбільшу зміну в підході до кешування з часу впровадження App Router. Попередня модель кешувала все за замовчуванням і вимагала явної відмови. Нова модель не кешує нічого за замовчуванням і вимагає явного увімкнення за допомогою директиви "use cache".

Фундаментальна зміна ментальної моделі

Next.js 16 переходить від неявного кешування (все кешується, відмова через динамічні API) до явного кешування (нічого не кешується, увімкнення через "use cache"). Ця єдина зміна впливає на маршрутизацію, отримання даних, рендеринг та формулювання питань на співбесідах.

Чому Next.js 16 замінив неявне кешування

Модель неявного кешування в Next.js 14-15 створювала проблеми з передбачуваністю. Виклик fetch всередині Server Component автоматично дедуплікувався та кешувався, але чи була сторінка статичною чи динамічною залежало від використаних API. Налагодження поведінки кешу вимагало розуміння кількох прихованих шарів: кешу fetch, кешу повного маршруту та кешу маршрутизатора.

Next.js 16 видаляє всі три неявні кеші. Кожна сторінка рендериться динамічно при кожному запиті, якщо явно не позначена директивою "use cache". Експорт revalidate видалено. unstable_cache замінено усвідомленою компілятором директивою "use cache". Повний обсяг цих змін описано в офіційному блозі Next.js 16.

Ця зміна обмінює автоматичну оптимізацію на явний контроль. Продуктивність може спочатку знизитися в додатках, що покладалися на неявне кешування, але досвід налагодження суттєво покращується: кешований контент кешується тому, що код так каже, а не через евристики фреймворку.

Як працює директива use cache на трьох рівнях

Директива "use cache" працює на трьох рівнях: файлу, компонента та функції. Вибір правильного рівня — це найважливіше рішення щодо кешування в Next.js 16.

Кешування на рівні файлу позначає кожен асинхронний експорт у файлі як кешований. Підходить для сторінок з виключно статичним контентом без даних, специфічних для користувача.

app/pricing/page.tsxtypescript
"use cache"

import { getPricingPlans } from "@/lib/data"

// Entire page is cached as a static shell
export default async function PricingPage() {
  const plans = await getPricingPlans()
  return (
    <section>
      {plans.map((plan) => (
        <PricingCard key={plan.id} plan={plan} />
      ))}
    </section>
  )
}

Кешування на рівні компонента кешує окремі компоненти на сторінці. Це уможливлює Partial Pre-Rendering: кешований компонент рендериться як частина статичної оболонки, тоді як динамічні сусіди передаються потоком під час запиту.

components/ProductRecommendations.tsxtsx
async function ProductRecommendations({ categoryId }: { categoryId: string }) {
  "use cache"
  // categoryId becomes part of the automatic cache key
  const products = await getTopProducts(categoryId)
  return (
    <ul>
      {products.map((p) => (
        <li key={p.id}>{p.name} - {p.price}</li>
      ))}
    </ul>
  )
}

Кешування на рівні функції спрямоване безпосередньо на функції отримання даних. Замінює старий патерн unstable_cache.

lib/data.tstypescript
import { cacheLife } from "next/cache"

export async function getArticleBySlug(slug: string) {
  "use cache"
  cacheLife("hours")
  // slug is automatically included in the cache key
  const article = await db.article.findUnique({ where: { slug } })
  return article
}

Компілятор автоматично генерує ключі кешування з аргументів функції. Жодних ручних масивів keyParts, жодних обхідних рішень з JSON.stringify. Аргументи повинні бути серіалізованими (рядки, числа, прості обʼєкти). Передача екземпляра класу або функції як аргументу порушує серіалізацію.

Partial Pre-Rendering: статична оболонка з динамічними отворами

Partial Pre-Rendering (PPR) був експериментальним у Next.js 14-15. У Next.js 16 PPR є стабільним і інтегрованим безпосередньо з Cache Components через cacheComponents: true у next.config.ts.

PPR дозволяє одному маршруту бути частково статичним і частково динамічним одночасно. Статична оболонка подається миттєво з CDN. Динамічний контент передається потоком у міру розвʼязання меж <Suspense>.

app/dashboard/page.tsxtsx
import { Suspense } from "react"
import { UserGreeting } from "@/components/UserGreeting"
import { StaticSidebar } from "@/components/StaticSidebar"
import { RecentActivity } from "@/components/RecentActivity"

export default function DashboardPage() {
  return (
    <div className="grid grid-cols-12 gap-6">
      {/* Cached static shell - served instantly */}
      <StaticSidebar />

      <main className="col-span-9">
        {/* Dynamic - streams in after static shell */}
        <Suspense fallback={<GreetingSkeleton />}>
          <UserGreeting />
        </Suspense>

        {/* Dynamic - streams independently */}
        <Suspense fallback={<ActivitySkeleton />}>
          <RecentActivity />
        </Suspense>
      </main>
    </div>
  )
}

Дерево рішень рендерингу є простим: компоненти з "use cache" стають частиною статичної оболонки. Компоненти, загорнуті в <Suspense>, що читають cookies, headers або інші дані, специфічні для запиту, передаються потоком динамічно. Все інше рендериться під час запиту.

Прапорець experimental.ppr та конфігурація сегмента маршруту experimental_ppr з Next.js 15 видалені. Поведінка PPR тепер контролюється виключно через cacheComponents: true.

Увімкнення Cache Components

Необхідно додати cacheComponents: true до next.config.ts. Цей єдиний прапорець вмикає PPR, директиву "use cache" та всю систему Cache Components. Жодна додаткова конфігурація не потрібна.

Профілі cacheLife: заміна revalidate

Експорт revalidate з Next.js 15 видалено. Натомість cacheLife() пропонує іменовані профілі для контролю тривалості кешу. Вбудовані профілі: seconds, minutes, hours, days, weeks та max.

lib/data.tstypescript
import { cacheLife } from "next/cache"

export async function getExchangeRates() {
  "use cache"
  cacheLife("minutes") // Revalidates every few minutes
  const rates = await fetch("https://api.exchangerate.host/latest")
  return rates.json()
}

export async function getCompanyInfo() {
  "use cache"
  cacheLife("weeks") // Rarely changes
  return db.company.findFirst()
}

Користувацькі профілі визначаються в next.config.ts:

next.config.tstypescript
import type { NextConfig } from "next"

const config: NextConfig = {
  cacheComponents: true,
  cacheLife: {
    // Custom profile for product data
    product: {
      stale: 300,      // Serve stale for 5 minutes
      revalidate: 3600, // Revalidate in background every hour
      expire: 86400,    // Hard expire after 24 hours
    },
  },
}

export default config

Централізація профілів у конфігурації означає, що одна зміна коригує кешування по всьому додатку. Це усуває розкидані значення revalidate: 3600, які були проблемою кодових баз Next.js 15.

Одне правило, яке слід запамʼятати: cacheLife() повинен виконуватися лише один раз за виклик функції. Умовне кешування є допустимим лише тоді, коли виконується одна гілка:

typescript
export async function getProduct(id: string) {
  "use cache"
  const product = await db.product.findUnique({ where: { id } })
  if (!product) {
    // Short cache for missing items (may appear soon)
    cacheLife("minutes")
    return null
  }
  // Longer cache for existing products
  cacheLife("product")
  return product
}

Готовий до співбесід з React / Next.js?

Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.

cacheTag для цільової інвалідації

Без cacheTag() кешована функція може закінчити термін дії лише за часом. Інвалідація за запитом вимагає тегування записів кешу та виклику revalidateTag() у Server Action.

lib/data.tstypescript
import { cacheLife, cacheTag } from "next/cache"

export async function getProductById(id: string) {
  "use cache"
  cacheTag(`product-${id}`, "products")
  cacheLife("days")
  return db.product.findUnique({ where: { id } })
}
app/actions.tstypescript
"use server"

import { revalidateTag } from "next/cache"

export async function updateProduct(id: string, data: ProductUpdate) {
  await db.product.update({ where: { id }, data })
  // Invalidate this specific product AND the product list
  revalidateTag(`product-${id}`)
  revalidateTag("products")
}

Теги підтримують до 256 символів кожен, з максимумом 128 тегів на запис кешу. Практична конвенція: теги на рівні сутності (product-123) для окремих записів та теги на рівні колекції (products) для сторінок зі списками.

Пастка відсутніх тегів

Кешована функція без cacheTag() може закінчити термін дії лише за часом. Інвалідація за запитом неможлива. Це легко пропустити під час початкової розробки та болісно виявити, коли клієнт повідомляє про застарілі дані на продакшені.

Безпека: use cache проти use cache private

За замовчуванням директива "use cache" створює спільний кеш. Будь-яка комбінація аргументів створює запис кешу, який може бути поданий будь-якому користувачу. Це правильно для публічних даних, але небезпечно для персоналізованого контенту.

"use cache: private" створює кеш на кожного користувача, який включає поточну сесію в ключ кешу. Може безпечно звертатися до cookies() та headers() в кешованому обсязі.

typescript
// WRONG: User data in shared cache - data leak risk
export async function getUserDashboard(userId: string) {
  "use cache"
  return db.user.findUnique({
    where: { id: userId },
    include: { orders: true, preferences: true },
  })
}

// CORRECT: Private cache scoped to the current user
export async function getUserDashboard() {
  "use cache: private"
  cacheLife("minutes")
  const session = await cookies()
  const userId = session.get("userId")?.value
  return db.user.findUnique({
    where: { id: userId },
    include: { orders: true, preferences: true },
  })
}

Третій варіант, "use cache: remote", зберігає кеш у зовнішньому сховищі. У безсерверних середовищах (Vercel, AWS Lambda) кеш у памʼяті за замовчуванням втрачається при холодному старті. "use cache: remote" забезпечує збереження записів кешу між екземплярами функцій.

Матриця рішень для співбесід:

| Директива | Обсяг | Коли використовувати | |-----------|-------|---------------------| | "use cache" | Спільний, усі користувачі | Публічні дані: прайси, статті, каталоги продуктів | | "use cache: private" | Сесія на користувача | Персоналізовані дані: дашборди, налаштування, історія замовлень | | "use cache: remote" | Спільний, зовнішнє сховище | Дані з високим трафіком у безсерверних середовищах |

Міграція з Next.js 15 до Cache Components

Міграція з unstable_cache до "use cache" здебільшого механічна, але має три пастки, які часто зʼявляються на співбесідах.

Пастка 1: Замикання над даними обсягу запиту. Якщо кешована функція посилається на значення з оточуючого обсягу, яке змінюється при кожному запиті (заголовок, cookie, ідентифікатор користувача), конвертація не є прямою. Необхідно передати це значення як явний аргумент або використати "use cache: private".

Пастка 2: Умовне кешування. Код, який огортав unstable_cache лише за певної умови, потребує реструктуризації. "use cache" завжди активний після застосування. Умову потрібно винести за межі кешованої функції.

Пастка 3: Ручні ключі кешу. unstable_cache вимагав явних масивів keyParts. Компілятор "use cache" генерує ключі автоматично з аргументів плюс ідентифікатор збірки та хеш функції. Видалення ручного управління ключами є метою, але необхідно перевірити, що всі значення, які розрізняють кеш, є фактичними параметрами функції.

Офіційний посібник оновлення до версії 16 охоплює повний шлях міграції.

Питання для співбесід: що запитують у senior-розробників

Ці питання відображають реальні паттерни співбесід 2026 року для senior-позицій Next.js. Кожне спрямоване на конкретний аспект Cache Components.

П1: Поясніть перехід від неявного до явного кешування в Next.js 16. Чому фреймворк зробив цю зміну?

Next.js 14-15 кешував виклики fetch та сторінки неявно. Налагодження того, чи є сторінка статичною чи динамічною, вимагало відстеження через кілька прихованих шарів кешу. Явна модель з "use cache" робить кешування видимим у вихідному коді. Компроміс: продуктивність може спочатку знизитися в додатках, що мігрують з неявного кешування, але розробники отримують повний контроль та передбачуваність.

П2: Які три рівні "use cache" і коли слід використовувати кожен?

Рівень файлу для повністю статичних сторінок. Рівень компонента для змішування кешованого та динамічного контенту на сторінці (патерн PPR). Рівень функції для кешування конкретних операцій отримання даних. Вибір рівня визначає гранулярність кешу та межі інвалідації.

П3: Команда кешує функцію, що повертає історію замовлень користувача, за допомогою "use cache". Що відбудеться?

Спільний кеш зберігає результат з ключем на основі аргументів функції. Якщо функція приймає параметр userId, різні користувачі отримують різні записи кешу, але інфраструктура кешу залишається спільною. Якщо функція читає userId з cookies() замість параметрів, збірка завершується помилкою, оскільки cookies() є runtime API, забороненим в обсязі спільного кешу. Розвʼязання: перейти на "use cache: private" або передати ідентифікатор користувача як явний аргумент.

П4: Чим cacheLife відрізняється від старого експорту revalidate?

revalidate була єдиним числом (секунди), що встановлювалося на рівні сторінки або layout. cacheLife використовує іменовані профілі з трьома вимірами: stale (подавати застарілий контент), revalidate (інтервал фонового оновлення) та expire (жорстке закінчення терміну). Профілі централізовані в next.config.ts, тому одна зміна впливає на всі місця виклику, що використовують цей профіль.

П5: Опишіть, як PPR рендерить сторінку дашборду зі статичною бічною панеллю та динамічним контентом користувача.

Під час збірки Next.js генерує статичну оболонку, що містить бічну панель (позначену "use cache") та fallback <Suspense> для динамічних секцій. При запиті CDN подає статичну оболонку миттєво. Сервер потім передає потоком динамічний контент (привітання користувача, стрічку активності) в межі Suspense. Користувач бачить макет миттєво, а динамічний контент заповнюється поступово.

П6: Що відбувається з кешованою функцією без cacheTag()?

Вона може закінчити термін дії лише за часом через профіль cacheLife. Інвалідація за запитом через revalidateTag() неможлива. Це поширена помилка, яка виявляється, коли редактори контенту оновлюють запис та очікують миттєвого відображення на сайті. Кожна кешована функція, яка може потребувати інвалідації за запитом, повинна містити принаймні один cacheTag().

Більше питань для співбесід з отримання даних у Next.js доступно на SharpSkill з практичними модулями, що включають сесії з таймером та детальні пояснення. Модуль Server Actions Next.js охоплює патерни Server Action, що працюють з revalidateTag.

Практичний чек-лист для продакшн Cache Components

  • Увімкнення cacheComponents: true у next.config.ts та видалення прапорців experimental.ppr та experimental.dynamicIO
  • Аудит кожної сторінки: додавання "use cache" до статичних сторінок та функцій отримання даних, що обслуговують публічний контент
  • Огортання всього динамічного контенту (специфічного для користувача, часу запиту) в межі <Suspense> зі змістовними скелетними fallback
  • Використання "use cache: private" для кожної функції, що звертається до cookies, headers або повертає персоналізовані дані
  • Визначення користувацьких профілів cacheLife для типових категорій даних (дані продуктів, сесії користувачів, статичний контент)
  • Додавання cacheTag() до кожної кешованої функції, яка може потребувати інвалідації за запитом
  • Тестування в продакшн режимі з next build && next start, оскільки поведінка кешування в next dev суттєво відрізняється
  • Моніторинг коефіцієнтів попадань кешу на сторінку протягом 24 годин перед розгортанням на наступну сторінку

Починай практикувати!

Перевір свої знання з нашими симуляторами співбесід та технічними тестами.

Висновок

  • Next.js 16 замінює неявне кешування явним "use cache" на рівні файлу, компонента та функції
  • PPR тепер стабільний та за замовчуванням під cacheComponents: true, доставляючи статичні оболонки з потоковим динамічним контентом
  • Профілі cacheLife замінюють revalidate централізованим, тривимірним контролем тривалості кешу
  • cacheTag + revalidateTag забезпечують інвалідацію за запитом; відсутні теги означають лише закінчення терміну за часом
  • "use cache: private" є обовʼязковим для даних, специфічних для користувача, щоб запобігти витоку даних між користувачами
  • Питання на співбесідах 2026 року фокусуються на переході від неявного до явного кешування, потоці рендерингу PPR, безпеці кешу та пастках міграції з Next.js 15

Починай практикувати!

Перевір свої знання з нашими симуляторами співбесід та технічними тестами.

Теги

#next.js
#react
#cache-components
#ppr
#interview

Поділитися

Пов'язані статті