Получение, кэширование и ревалидация данных
Получение данных — это ключевая часть любого приложения. На этой странице объясняется, как можно получать, кэшировать и ревалидировать данные в React и Next.js.
Существует четыре способа получения данных:
- На сервере с помощью
fetch
- На сервере с помощью сторонних библиотек
- На клиенте через Route Handler
- На клиенте с помощью сторонних библиотек.
Получение данных на сервере с fetch
Next.js расширяет нативный Web API fetch
, позволяя настраивать поведение кэширования и ревалидации для каждого запроса на сервере. React расширяет fetch
, автоматически мемоизируя запросы при рендеринге дерева React-компонентов.
Вы можете использовать fetch
с async
/await
в Server Components, Route Handlers и Server Actions.
Например:
async function getData() {
const res = await fetch('https://api.example.com/...')
// Возвращаемое значение *не* сериализуется
// Можно возвращать Date, Map, Set и т.д.
if (!res.ok) {
// Активирует ближайший Error Boundary из `error.js`
throw new Error('Не удалось получить данные')
}
return res.json()
}
export default async function Page() {
const data = await getData()
return <main></main>
}
async function getData() {
const res = await fetch('https://api.example.com/...')
// Возвращаемое значение *не* сериализуется
// Можно возвращать Date, Map, Set и т.д.
if (!res.ok) {
// Активирует ближайший Error Boundary из `error.js`
throw new Error('Не удалось получить данные')
}
return res.json()
}
export default async function Page() {
const data = await getData()
return <main></main>
}
Полезно знать:
- Next.js предоставляет полезные функции, которые могут понадобиться при получении данных в Server Components, такие как
cookies
иheaders
. Они приводят к динамическому рендерингу маршрута, так как зависят от информации о времени запроса.- В Route Handlers запросы
fetch
не мемоизируются, так как Route Handlers не являются частью дерева React-компонентов.- В Server Actions запросы
fetch
не кэшируются (по умолчаниюcache: no-store
).- Для использования
async
/await
в Server Component с TypeScript требуется TypeScript5.1.3
или выше и@types/react
18.2.8
или выше.
Кэширование данных
Кэширование сохраняет данные, чтобы их не нужно было повторно запрашивать из источника при каждом запросе.
По умолчанию Next.js автоматически кэширует возвращаемые значения fetch
в Data Cache на сервере. Это означает, что данные могут быть получены во время сборки или запроса, закэшированы и повторно использованы при каждом запросе данных.
// 'force-cache' используется по умолчанию и может быть опущен
fetch('https://...', { cache: 'force-cache' })
Однако есть исключения — запросы fetch
не кэшируются, когда:
- Используются внутри Server Action.
- Используются внутри Route Handler с методом
POST
.
Что такое Data Cache?
Data Cache — это постоянный HTTP-кэш. В зависимости от платформы кэш может масштабироваться автоматически и распределяться между несколькими регионами.
Подробнее о Data Cache.
Ревалидация данных
Ревалидация — это процесс очистки Data Cache и повторного получения актуальных данных. Это полезно, когда ваши данные изменяются и вы хотите убедиться, что отображается самая свежая информация.
Закэшированные данные можно ревалидировать двумя способами:
- Ревалидация по времени: Автоматическая ревалидация данных через определенный промежуток времени. Полезно для данных, которые изменяются редко, и их актуальность не критична.
- Ревалидация по запросу: Ручная ревалидация данных на основе события (например, отправки формы). Ревалидация по запросу может использовать теги или пути для одновременной ревалидации групп данных. Полезно, когда нужно как можно быстрее показать актуальные данные (например, при обновлении контента из headless CMS).
Ревалидация по времени
Для ревалидации данных через определенные интервалы можно использовать опцию next.revalidate
в fetch
, чтобы установить время жизни ресурса в кэше (в секундах).
fetch('https://...', { next: { revalidate: 3600 } })
Альтернативно, для ревалидации всех запросов fetch
в сегменте маршрута можно использовать Segment Config Options.
export const revalidate = 3600 // ревалидировать не чаще чем раз в час
Если в статически рендерящемся маршруте есть несколько запросов fetch
с разной частотой ревалидации, для всех запросов будет использовано наименьшее время. Для динамически рендерящихся маршрутов каждый запрос fetch
будет ревалидироваться независимо.
Подробнее о ревалидации по времени.
Ревалидация по запросу
Данные можно ревалидировать по запросу по пути (revalidatePath
) или по тегу кэша (revalidateTag
) внутри Server Action или Route Handler.
Next.js имеет систему тегирования кэша для инвалидации запросов fetch
между маршрутами.
- При использовании
fetch
можно пометить записи кэша одним или несколькими тегами. - Затем можно вызвать
revalidateTag
, чтобы ревалидировать все записи, связанные с этим тегом.
Например, следующий запрос fetch
добавляет тег кэша collection
:
export default async function Page() {
const res = await fetch('https://...', { next: { tags: ['collection'] } })
const data = await res.json()
// ...
}
export default async function Page() {
const res = await fetch('https://...', { next: { tags: ['collection'] } })
const data = await res.json()
// ...
}
Затем можно ревалидировать этот вызов fetch
с тегом collection
, вызвав revalidateTag
в Server Action:
'use server'
import { revalidateTag } from 'next/cache'
export default async function action() {
revalidateTag('collection')
}
'use server'
import { revalidateTag } from 'next/cache'
export default async function action() {
revalidateTag('collection')
}
Подробнее о ревалидации по запросу.
Обработка ошибок и ревалидация
Если при попытке ревалидации данных возникает ошибка, из кэша будут продолжать обслуживаться последние успешно сгенерированные данные. При следующем запросе Next.js повторно попытается ревалидировать данные.
Отказ от кэширования данных
Запросы fetch
не кэшируются, если:
- Добавлен параметр
cache: 'no-store'
в запросfetch
. - Добавлена опция
revalidate: 0
в отдельный запросfetch
. - Запрос
fetch
находится внутри Route Handler с методомPOST
. - Запрос
fetch
выполняется после использованияheaders
илиcookies
. - Используется опция сегмента маршрута
const dynamic = 'force-dynamic'
. - Опция сегмента маршрута
fetchCache
настроена на пропуск кэширования по умолчанию. - Запрос
fetch
использует заголовкиAuthorization
илиCookie
, и выше в дереве компонентов есть некэшируемый запрос.
Отдельные запросы fetch
Чтобы отказаться от кэширования для отдельных запросов fetch
, можно установить параметр cache
в 'no-store'
. Это позволит получать данные динамически при каждом запросе.
fetch('https://...', { cache: 'no-store' })
Все доступные параметры cache
можно посмотреть в справочнике API fetch
.
Несколько запросов fetch
Если в сегменте маршрута (например, в Layout или Page) есть несколько запросов fetch
, можно настроить поведение кэширования для всех запросов данных в сегменте с помощью Segment Config Options.
Однако рекомендуется настраивать поведение кэширования для каждого запроса fetch
индивидуально. Это обеспечивает более детальный контроль над кэшированием.
Получение данных на сервере со сторонними библиотеками
Если вы используете стороннюю библиотеку, которая не поддерживает или не предоставляет доступ к fetch
(например, клиент базы данных, CMS или ORM), можно настроить поведение кэширования и ревалидации таких запросов с помощью Route Segment Config Option и функции React cache
.
Будет ли кэшироваться результат запроса, зависит от того, статически или динамически рендерится сегмент маршрута. Если сегмент статический (по умолчанию), результат запроса будет кэшироваться и ревалидироваться как часть сегмента маршрута. Если сегмент динамический, результат запроса не будет кэшироваться и будет перезапрашиваться при каждом рендеринге сегмента.
Также можно использовать экспериментальный API unstable_cache
.
Пример
В следующем примере:
- Функция React
cache
используется для мемоизации запросов данных. - Опция
revalidate
установлена в3600
в сегментах Layout и Page, что означает, что данные будут кэшироваться и ревалидироваться не чаще чем раз в час.
import { cache } from 'react'
export const getItem = cache(async (id: string) => {
const item = await db.item.findUnique({ id })
return item
})
import { cache } from 'react'
export const getItem = cache(async (id) => {
const item = await db.item.findUnique({ id })
return item
})
Хотя функция getItem
вызывается дважды, будет выполнен только один запрос к базе данных.
import { getItem } from '@/utils/get-item'
export const revalidate = 3600 // ревалидировать данные не чаще чем раз в час
export default async function Layout({
params: { id },
}: {
params: { id: string }
}) {
const item = await getItem(id)
// ...
}
import { getItem } from '@/utils/get-item'
export const revalidate = 3600 // ревалидировать данные не чаще чем раз в час
export default async function Layout({ params: { id } }) {
const item = await getItem(id)
// ...
}
import { getItem } from '@/utils/get-item'
export const revalidate = 3600 // ревалидировать данные не чаще чем раз в час
export default async function Page({
params: { id },
}: {
params: { id: string }
}) {
const item = await getItem(id)
// ...
}
import { getItem } from '@/utils/get-item'
export const revalidate = 3600 // ревалидировать данные не чаще чем раз в час
export default async function Page({ params: { id } }) {
const item = await getItem(id)
// ...
}
Получение данных на клиенте с Route Handlers
Если нужно получить данные в клиентском компоненте, можно вызвать Route Handler из клиента. Route Handlers выполняются на сервере и возвращают данные клиенту. Это полезно, когда не хочется раскрывать конфиденциальную информацию клиенту, например, API-токены.
Примеры можно найти в документации по Route Handlers.
Server Components и Route Handlers
Поскольку Server Components рендерятся на сервере, вам не нужно вызывать Route Handler из Server Component для получения данных. Вместо этого можно получать данные напрямую внутри Server Component.
Получение данных на клиенте со сторонними библиотеками
Также можно получать данные на клиенте с помощью сторонних библиотек, таких как SWR или TanStack Query. Эти библиотеки предоставляют собственные API для мемоизации запросов, кэширования, ревалидации и мутации данных.
Будущие API:
use
— это функция React, которая принимает и обрабатывает промис, возвращаемый функцией. Обертываниеfetch
вuse
в настоящее время не рекомендуется в клиентских компонентах и может вызывать множественные ререндеры. Подробнее оuse
в документации React.