Перейти к содержанию
Innovialabs

Загрузка данных и кэширование в Next.js: подробный разбор

Продвинутое4 мин

Коротко

В Next.js вы сами решаете, что кэшировать и как долго: данные можно обновлять по таймеру или по тегу после изменения, а динамические части стримить через Suspense. Устаревшие данные почти всегда объясняются тем, что страница или запрос неожиданно оказались закэшированы.

Коротко: как Next.js получает и хранит данные

В App Router данные загружаются прямо в серверных компонентах: компонент может быть async, а внутри можно вызвать fetch, ORM или любой SDK. Next.js добавляет к этому несколько слоёв кэша, и именно они определяют, увидит ли пользователь свежие данные.

Важно: поведение по умолчанию менялось между мажорными версиями Next.js. В одних версиях fetch кэшировался автоматически, в других — нет, а в новых появилась директива "use cache". Поэтому перед работой сверяйтесь с документацией именно вашей версии, а в коде задавайте поведение явно.

Три вопроса, которые стоит задать каждому запросу

  1. Можно ли эти данные переиспользовать между пользователями? Каталог товаров — да. Корзина конкретного человека — нет.
  2. Как долго они могут быть устаревшими? Курс валют — минуты, статья блога — часы или до момента редактирования.
  3. Что должно сбрасывать кэш? Таймер или конкретное событие: публикация, оплата, изменение цены.

Ответы на эти вопросы и определяют стратегию.

Ревалидация по времени

Подходит, когда данные меняются регулярно, но точный момент изменения вам неизвестен.

export default async function Prices() {
  const res = await fetch("https://api.example.com/prices", {
    next: { revalidate: 300 },
  });
  const prices = await res.json();
  return <PriceTable data={prices} />;
}

Логика stale-while-revalidate: в течение 300 секунд отдаётся кэш, после этого первый запрос всё ещё получает старую версию, а в фоне готовится новая. Значит, один пользователь может увидеть данные чуть старше заданного интервала — это нормально.

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

Подходит, когда вы точно знаете, когда данные изменились: админ сохранил статью, пришёл webhook из CRM.

// Загрузка
const res = await fetch("https://api.example.com/posts", {
  next: { tags: ["posts"] },
});

// Server Action после сохранения
"use server";
import { revalidateTag } from "next/cache";

export async function savePost(data: FormData) {
  await db.post.update(/* ... */);
  revalidateTag("posts", "max");
}

В свежих версиях revalidateTag принимает второй аргумент — профиль кэша, в старых — только тег. Есть и revalidatePath("/blog"), который сбрасывает кэш конкретного маршрута. Теги удобнее: один тег можно повесить на десятки запросов на разных страницах.

Статический и динамический рендеринг

  • Статический — страница собирается при билде или при ревалидации и отдаётся из кэша. Быстро и дёшево.
  • Динамический — страница рендерится на каждый запрос. Нужен, когда ответ зависит от пользователя.

Страница становится динамической, если вы используете cookies(), headers(), searchParams или запрос без кэша (cache: "no-store"). Частая ловушка: одно обращение к cookies() в layout делает динамическим весь вложенный раздел.

СитуацияЧто выбрать
Лендинг, документацияСтатика
Блог с редактированием из CMSСтатика + теги
Каталог с ценами из APIСтатика + revalidate по времени
Личный кабинет, корзинаДинамика

Стриминг с Suspense

Если на странице есть медленный блок, не заставляйте всю страницу ждать его. Оберните его в Suspense — остальная часть придёт сразу, а медленная догрузится потоком.

import { Suspense } from "react";

export default function Dashboard() {
  return (
    <>
      <Header />
      <Suspense fallback={<Skeleton />}>
        <SlowReport />
      </Suspense>
    </>
  );
}

Файл loading.tsx в папке маршрута делает то же самое для всей страницы. Совет: запускайте независимые запросы параллельно через Promise.all, а не цепочкой await, иначе получится «водопад» задержек.

Как отлаживать устаревшие данные

  • Проверьте, статическая ли страница. Вывод next build показывает тип каждого маршрута.
  • Проверяйте в production-режиме. В next dev кэш ведёт себя иначе, поэтому баги часто видны только после next build && next start.
  • Убедитесь, что тег совпадает при загрузке и при ревалидации — опечатка в строке ломает всё молча.
  • Не забывайте про клиентский роутер. После мутации вызывайте ревалидацию на сервере или router.refresh(), иначе клиент может показать прежний результат.
  • Учитывайте CDN. Если перед приложением стоит CDN со своими правилами, он может держать старый ответ независимо от Next.js.
  • Логируйте. Временный console.log в серверном компоненте сразу показывает, выполнился ли он заново.

Частые ошибки

  • Кэширование персональных данных в общем кэше — риск показать чужие данные.
  • no-store везде «на всякий случай» — сайт становится медленным и дорогим.
  • Последовательные await вместо параллельной загрузки.
  • Надежда на поведение по умолчанию вместо явных настроек.

Частые вопросы

Да, если данные общие и не меняются каждую секунду. Для запросов не через fetch используйте механизмы кэширования функций вашей версии Next.js и сбрасывайте их тегами после изменений.

Поможем с этим

Если хотите не разбираться самим, а получить результат — этим мы занимаемся каждый день.

Обсудить проект

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.