Capítulo 28 de 456
Modelo de cache para projetos SEM cacheComponents habilitado: cache de fetch via opção cache, cache de funções não-fetch via unstable_cache, e controle por route segment config (dynamic, fetchCache, revalidate).
fetch(url, { cache: 'force-cache' }): por padrão fetch não é cacheado; force-cache cacheia a requisição individual.unstable_cache(fn, keyParts, { tags, revalidate }): cacheia funções async que não usam fetch (ORM, DB direto); segundo argumento é prefixo da chave de cache.dynamic: route segment config ('auto' | 'force-dynamic' | 'error' | 'force-static') que controla o comportamento de renderização de toda a rota.fetchCache: opção avançada que sobrescreve o cache default de todos os fetch de uma rota ('auto', 'default-cache', 'only-cache', 'force-cache', 'default-no-store', 'only-no-store', 'force-no-store').revalidate (route segment config): false (padrão, cache indefinido) | 0 (sempre dinâmico) | number (segundos); o menor revalidate entre layouts/pages de uma rota domina a rota inteira.revalidateTag/revalidatePath: invalidação on-demand chamadas em Server Action ou Route Handler.cache(): deduplica chamadas não-fetch (ORM/DB) dentro de um único render pass (fetch já é memoizado automaticamente).server-only + React cache + função preload() chamada antes de trabalho bloqueante, pra iniciar fetch cedo.export const getCachedUser = unstable_cache(
async (id: string) => db.select().from(users).where(eq(users.id, id)).then(r => r[0]),
['user'],
{ tags: ['user'], revalidate: 3600 }
)
fetch) com tag para invalidação e revalidação temporal.export const dynamic = 'force-dynamic'
// equivalente a: cache: 'no-store' em todo fetch + fetchCache = 'force-no-store'
export const getPost = cache(async (id: string) => {
return db.query.posts.findFirst({ where: eq(posts.id, parseInt(id)) })
})
export const preload = (id: string) => { void getItem(id) }
// chamar preload(id) antes de await checkIsAvailable() para iniciar fetch cedo
import { revalidateTag } from 'next/cache'
export async function updateUser(id: string) {
revalidateTag('user', 'max')
}
Opção dynamic | Efeito |
|---|---|
'auto' (padrão) | Cacheia o máximo possível sem forçar opt-in dinâmico |
'force-dynamic' | Renderiza por request; equivale a cache:'no-store' em todo fetch |
'error' | Força prerender; erro se algo usar API de request-time ou dado não cacheado |
'force-static' | Força prerender; cookies()/headers()/useSearchParams() retornam vazio |
Opção revalidate | Efeito |
|---|---|
false (padrão) | Cache indefinido (equivale a Infinity); fetch individual pode usar no-store/revalidate:0 |
0 | Sempre dinâmico; muda default de fetch sem cache para 'no-store' |
number (segundos) | Revalidação a cada N segundos; deve ser estaticamente analisável (600, não 60*10) |
'only-cache'+'only-no-store' ou 'force-cache'+'force-no-store' na mesma rota: não permitido, gera erro.'default-no-store' e child com 'auto'/'*-cache': comportamento inconsistente do mesmo fetch.revalidate = 60 * 10: não é estaticamente analisável; usar revalidate = 600.revalidateTag/revalidatePath funcionar em dev: páginas em dev sempre renderizam sob demanda, nunca cacheadas.fetch, unstable_cache e route segment config, não use cache directive.dynamic e fetchCache operam em nível de rota inteira; revalidate de fetch individual pode ser mais agressivo que o default da rota, nunca menos.fetch é automática; para ORM/DB direto, usar React cache().revalidateTag/revalidatePath invalidam o cache do Next.js, não CDN, então CDN precisa de purge separado.cacheComponents: true.generateStaticParams + revalidate deste modelo pra prerenderizar rotas dinâmicas.