Пошук уроків, статей та іншого контенту
Застосуєте директиву use cache для декларативного кешування компонентів, функцій і результатів.
use cacheuse cache — це директива Next.js для декларативного кешування:
серверних компонентів;
асинхронних функцій;
результатів обчислень або отримання даних.
Директива повідомляє Next.js, що результат потрібно зберігати й повторно використовувати під час наступних запитів.
async function getProducts() {
'use cache'
const response = await fetch('https://example.com/api/products')
return response.json()
}На відміну від звичайного мемоізування в пам’яті одного процесу, кеш Next.js може використовуватися між різними запитами та екземплярами застосунку.
Для використання use cache потрібно ввімкнути Cache Components у конфігурації Next.js.
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfigПісля зміни конфігурації перезапустіть сервер розробки.
Директива розміщується на початку тіла функції:
async function getProduct(id: string) {
'use cache'
const response = await fetch(`https://api.example.com/products/${id}`)
if (!response.ok) {
throw new Error('Не вдалося отримати товар')
}
return response.json()
}Результат буде кешуватися для кожного значення аргументу id окремо.
Виклик:
const firstProduct = await getProduct('1')
const secondProduct = await getProduct('2')створить два різні записи кешу.
Ключ кешу формується на основі, зокрема:
самої кешованої функції;
її аргументів;
значень, замкнених у функції;
версії збірки застосунку.
Тому аргументи кешованої функції мають бути стабільними та серіалізованими.
use cache можна застосовувати безпосередньо до асинхронного серверного компонента:
async function ProductList() {
'use cache'
const response = await fetch('https://api.example.com/products')
if (!response.ok) {
throw new Error('Не вдалося отримати товари')
}
const products: { id: number; title: string }[] = await response.json()
return (
<ul>
{products.map((product) => (
<li key={product.id}>{product.title}</li>
))}
</ul>
)
}У цьому випадку кешується результат рендерингу компонента, а не лише відповідь fetch.
Директиву можна розмістити на початку файлу:
'use cache'
export async function getProducts() {
// ...
}
export async function getCategories() {
// ...
}Тоді всі експортовані функції цього модуля розглядаються як кешовані.
Файлова форма зручна для модуля, який містить лише кешовані функції. Якщо в одному файлі є кешовані й некешовані функції, краще використовувати директиву всередині конкретних функцій.
За замовчуванням Next.js використовує стандартні правила кешування. Для явного керування часом життя використовуйте cacheLife з пакета next/cache.
import { cacheLife } from 'next/cache'
async function getProducts() {
'use cache'
cacheLife({
stale: 60,
revalidate: 3600,
expire: 86400,
})
const response = await fetch('https://api.example.com/products')
if (!response.ok) {
throw new Error('Не вдалося отримати товари')
}
return response.json()
}Параметри означають:
stale — скільки секунд клієнт може використовувати застаріле значення;
revalidate — через скільки секунд Next.js може перевірити дані повторно;
expire — максимальний час життя запису кешу.
Значення задаються в секундах.
Для повторного використання однакової політики в кількох місцях можна оголосити профіль кешу в next.config.ts:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
cacheLife: {
products: {
stale: 60,
revalidate: 3600,
expire: 86400,
},
},
}
export default nextConfigПісля цього профіль можна застосувати за його назвою:
import { cacheLife } from 'next/cache'
async function getProducts() {
'use cache'
cacheLife('products')
const response = await fetch('https://example.com/api/products')
if (!response.ok) {
throw new Error('Помилка API')
}
return response.json()
}Профіль з конфігурації описує політику, а не конкретні дані. Самі дані визначаються аргументами функції та її кодом.
Часове протермінування не завжди достатнє. Наприклад, після зміни товару потрібно одразу видалити пов’язані записи кешу.
Для цього використовуйте cacheTag:
import { cacheLife, cacheTag } from 'next/cache'
async function getProducts() {
'use cache'
cacheLife('products')
cacheTag('products')
const response = await fetch('https://example.com/api/products')
if (!response.ok) {
throw new Error('Не вдалося отримати товари')
}
return response.json()
}Тепер кешований результат пов’язаний із тегом products.
Тег можна зробити залежним від ідентифікатора:
import { cacheTag } from 'next/cache'
async function getProduct(id: string) {
'use cache'
cacheTag(`product:${id}`)
const response = await fetch(`https://example.com/api/products/${id}`)
if (!response.ok) {
throw new Error('Не вдалося отримати товар')
}
return response.json()
}У такому випадку товар із ідентифікатором 1 матиме тег product:1.
Для інвалідації використовується revalidateTag:
import { revalidateTag } from 'next/cache'
export async function POST() {
revalidateTag('products', 'max')
return Response.json({ revalidated: true })
}Після цього записи, пов’язані з тегом products, більше не вважаються актуальними. Під час наступного використання Next.js отримає свіжі дані відповідно до політики кешу.
Приклад повного мінімального застосунку:
// app/products/page.tsx
import { cacheLife, cacheTag } from 'next/cache'
type Product = {
id: number
title: string
}
async function getProducts(): Promise<Product[]> {
'use cache'
cacheLife({
stale: 60,
revalidate: 3600,
expire: 86400,
})
cacheTag('products')
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts?_limit=5',
)
if (!response.ok) {
throw new Error('Не вдалося отримати список товарів')
}
return response.json()
}
async function ProductList() {
'use cache'
const products = await getProducts()
return (
<ul>
{products.map((product) => (
<li key={product.id}>{product.title}</li>
))}
</ul>
)
}
export default async function ProductsPage() {
return (
<main>
<h1>Товари</h1>
<ProductList />
</main>
)
}// app/api/products/revalidate/route.ts
import { revalidateTag } from 'next/cache'
export async function POST() {
revalidateTag('products', 'max')
return Response.json({ revalidated: true })
}У цьому прикладі:
getProducts кешує результат отримання даних.
ProductList кешує результат серверного рендерингу компонента.
cacheLife задає час життя запису.
cacheTag зв’язує результат із тегом products.
POST /api/products/revalidate інвалідує записи з цим тегом.
У реальному застосунку endpoint для інвалідації потрібно захистити від несанкціонованих викликів.
Кешований код не повинен напряму залежати від даних конкретного HTTP-запиту, наприклад:
cookies();
headers();
поточного користувача;
інших значень, які змінюються для кожного запиту.
Такі дані можуть зробити результат неправильним для наступного користувача.
Натомість отримайте значення поза кешованою функцією та передайте його аргументом:
import { cookies } from 'next/headers'
async function getUserOrders(userId: string) {
'use cache'
const response = await fetch(
`https://example.com/api/users/${userId}/orders`,
)
if (!response.ok) {
throw new Error('Не вдалося отримати замовлення')
}
return response.json()
}
export default async function OrdersPage() {
const cookieStore = await cookies()
const userId = cookieStore.get('user-id')?.value
if (!userId) {
return <p>Потрібно увійти</p>
}
const orders = await getUserOrders(userId)
return <pre>{JSON.stringify(orders, null, 2)}</pre>
}Тут cookies() викликається в некешованому компоненті, а getUserOrders отримує userId як аргумент. Дані різних користувачів не змішуються, оскільки для різних userId створюються різні ключі кешу.
Не додавайте до кешу приватні дані без чіткого розуміння того, як формується ключ кешу.
Аргументи та результат кешованої функції мають бути сумісними із серіалізацією.
Надійними аргументами є:
рядки;
числа;
булеві значення;
null;
масиви;
звичайні об’єкти з такими значеннями.
Наприклад:
async function getArticle(id: number, locale: string) {
'use cache'
const response = await fetch(
`https://example.com/articles/${id}?locale=${locale}`,
)
if (!response.ok) {
throw new Error('Не вдалося отримати статтю')
}
return response.json()
}Не передавайте як аргументи кешованої функції об’єкти, які містять нестабільний стан або ресурси виконання, наприклад відкриті з’єднання чи об’єкти запиту.
Також не варто замикати в кешованій функції змінні, які змінюються між запитами. Значення, від яких залежить результат, краще передавати явно.
use cacheДиректива добре підходить для:
публічних сторінок, які рідко змінюються;
каталогів і списків;
результатів запитів до зовнішнього API;
дорогих серверних обчислень;
компонентів із передбачуваним результатом;
даних, які можна оновлювати за часом або тегом.
Не слід бездумно кешувати:
персональні сторінки;
дані, що залежать від cookies або заголовків;
результати, які повинні бути свіжими для кожного запиту;
операції зі змінними побічними ефектами.
Кешуйте читання даних, а не операції, які змінюють стан системи. Кешована функція може викликатися багато разів, тому вона має бути безпечною для повторного виконання.
Неправильно:
async function getData() {
console.log('Отримання даних')
'use cache'
return []
}Правильно:
async function getData() {
'use cache'
console.log('Отримання даних')
return []
}'use cache' має бути першим оператором у функції або першим оператором модуля.
Якщо директива не працює очікуваним чином, перевірте next.config.ts:
const nextConfig = {
cacheComponents: true,
}
export default nextConfigПісля зміни конфігурації потрібно перезапустити dev-сервер.
cookies() або headers() усередині кешованої функціїЦе створює залежність від конкретного запиту й може призвести до помилкового спільного кешу. Отримуйте такі значення в некешованому коді та передавайте їх як аргументи.
Неправильно:
const currentUserId = '42'
async function getOrders() {
'use cache'
return fetch(`/api/users/${currentUserId}/orders`)
}Краще:
async function getOrders(userId: string) {
'use cache'
return fetch(`/api/users/${userId}/orders`)
}Явні аргументи роблять залежності функції зрозумілими та впливають на ключ кешу.
Якщо дані можуть змінюватися до завершення revalidate, додайте cacheTag і викликайте revalidateTag після зміни даних.
use cache кешує серверні компоненти, функції та їхні результати.
Директива має бути першим оператором функції або модуля.
Для використання потрібно ввімкнути cacheComponents.
cacheLife визначає час життя запису.
cacheTag групує пов’язані записи кешу.
revalidateTag дає змогу інвалідувати кеш після зміни даних.
Дані запиту не слід читати безпосередньо всередині кешованого коду.
Значення, від яких залежить результат, потрібно передавати через аргументи.
Кешувати варто передбачувані операції читання без побічних ефектів.