spinny:~/writing $ vim nextjs-16-cache-components.md
1~2Durante años una de las preguntas más molestas en Next.js ha sido: "¿Esta página es estática o dinámica?". Parece una pregunta simple, hasta que agregas una llamada a `cookies()`, un `fetch` con diferentes opciones, un cliente de base de datos, un CMS, un carrito de compras o un contenido personalizado.3~4Next.js 16 es interesante porque intenta hacer que esta conversación sea menos misteriosa. No elimina la complejidad, pero cambia el modelo mental: las rutas son dinámicas de forma predeterminada, el caché se declara cuando es necesario y `Suspense` se convierte en la forma natural de componer shells rápidos con partes que se mantienen actualizadas.5~6La característica que hay que entender son los componentes de caché. Stable Turbopack, React Compiler, `proxy.ts` y las nuevas API de invalidación son importantes, pero giran en torno al mismo problema: crear aplicaciones rápidas sin tener que adivinar qué decidió el marco detrás de escena.7~8## Porque esto importa9~10En una aplicación real no sólo hay "páginas estáticas" y "páginas dinámicas". Tienes diferentes piezas con diferentes necesidades.11~12La ficha del producto puede cambiar algunas veces al día. El precio puede cambiar más a menudo. La disponibilidad debe ser casi activa. El nombre de usuario es personal. Las reseñas se pueden transmitir. La barra lateral puede ser estable. El carro no.13~14Si tratas todo como una unidad, siempre terminas en uno de dos extremos:15~16- almacenamiento en caché agresivo y riesgo de ver datos antiguos;17- renderizado dinámico en todas partes y rendimiento peor de lo necesario.18~19Cache Components sirve precisamente para evitar esta falsa elección.20~21## El modelo en la práctica.22~23Con `cacheComponents: true`, puedes declarar qué se puede almacenar en caché usando `"use cache"`. Luego puede asociar la duración y las etiquetas con `cacheLife()` y `cacheTag()`. Las partes dinámicas siguen siendo dinámicas y se pueden aislar con `Suspense`.24~25```mermaid26flowchart TD27 Request[Solicitud de usuario] --> Shell[shell en caché]28 Request --> Dynamic[Secciones dinámicas]29 Shell --> FirstPaint[Primer contenido rápido]30 Dynamic --> Stream[Streaming dentro del suspenso]31 Stream --> Complete[Página completa]32```33~34La configuración es pequeña:35~36```typescript37// next.config.ts38import type { NextConfig } from 'next';39~40const nextConfig: NextConfig = {41 cacheComponents: true,42};43~44export default nextConfig;45```46~47El gran cambio no está en la configuración. Está en cómo empiezas a escribir los componentes.48~49```tsx50// app/products/[slug]/page.tsx51import { Suspense } from 'react';52import { cacheLife, cacheTag } from 'next/cache';53~54async function getProduct(slug: string) {55 'use cache';56~57 cacheLife('hours');58 cacheTag(`product:${slug}`);59~60 return db.product.findUnique({ where: { slug } });61}62~63async function ProductDetails({ slug }: { slug: string }) {64 const product = await getProduct(slug);65~66 return (67 <section>68 <h1>{product.name}</h1>69 <p>{product.description}</p>70 </section>71 );72}73~74async function LiveInventory({ slug }: { slug: string }) {75 const inventory = await db.inventory.findFirst({ where: { slug } });76 return <p>{inventory.quantity} pezzi disponibili</p>;77}78~79export default async function ProductPage({ params }: { params: Promise<{ slug: string }> }) {80 const { slug } = await params;81~82 return (83 <>84 <ProductDetails slug={slug} />85 <Suspense fallback={<p>Controllo disponibilità...</p>}>86 <LiveInventory slug={slug} />87 </Suspense>88 </>89 );90}91```92~93No es necesario que la página esté toda en caché o dinámica. La ficha de producto puede ser rápida y reutilizable. El inventario puede mantenerse actualizado. El usuario ve algo de inmediato, sin esperar la parte más lenta.94~95## `use cache` es documentación ejecutable96~97Lo que me gusta de `"use cache"` es que te obliga a hacer explícita una intención. Cuando lees una función, comprendes inmediatamente que alguien ha decidido: "estos datos se pueden reutilizar".98~99Es especialmente útil cuando no estás usando `fetch`. Muchas aplicaciones leen datos de Prisma, Drizzle, SDK internos, clientes CMS o funciones de servicio. En esos casos, el antiguo razonamiento basado únicamente en las opciones `fetch` no era suficiente.100~101Una regla general:102~103- contenido de caché relativamente estable;104- utilizar etiquetas granulares;105- deja permisos dinámicos, sesiones, carritos, notificaciones y estados transaccionales;106- poner partes lentas dentro de `Suspense`;107- medir antes de decir "mejoramos el rendimiento".108~109## Invalidar sin tirarlo todo110~111El caché sólo es útil si puedes actualizarlo con precisión. Aquí `cacheTag`, `revalidateTag` y `updateTag` se vuelven importantes.112~113Ejemplo:114~115```typescript116'use server';117~118import { updateTag } from 'next/cache';119~120export async function updateProductName(productId: string, name: string) {121 await db.product.update({122 where: { id: productId },123 data: { name },124 });125~126 updateTag(`product:${productId}`);127}128```129~130El detalle importante es la etiqueta. `product:${productId}` indica un límite preciso. `products` dice un cubo enorme. Al principio el enorme cubo resulta cómodo; después de unos meses se convierte en el motivo por el que invalidas media aplicación para cambiar un título.131~132## Turbopack estable: la parte que escuchas todos los días133~134Next.js 16 lleva Turbopack al centro de desarrollo y construcción. No es la característica más poética, pero es la que sientes mientras trabajas: servidor que se inicia antes, actualización más rápida, compilaciones que dejan de parecer una pausa para el café forzada.135~136Dicho esto, no migraría una base de código llena de complementos personalizados con los ojos cerrados. Yo comprobaría:137~138- construcción local;139- importación no estándar;140- MDX, SVG y CSS;141- Quedan complementos de paquete web;142- páginas críticas;143- diferencias en los tiempos de construcción.144~145Para proyectos nuevos, comenzaría desde el valor predeterminado. Para los maduros, haría una migración medida.146~147## React Compiler: elimina el ruido, no el pensamiento148~149React Compiler 1.0 es estable y Next.js 16 lo admite con `reactCompiler`. La promesa es reducir mucha memorización manual: menos `memo`, menos `useMemo`, menos `useCallback` utilizados "por seguridad".150~151```typescript152// next.config.ts153import type { NextConfig } from 'next';154~155const nextConfig: NextConfig = {156 reactCompiler: true,157};158~159export default nextConfig;160```161~162No lo trataría como un polvo mágico. El compilador ayuda cuando el código sigue bien las reglas de React. Si los componentes tienen efectos secundarios extraños, mutaciones ocultas o ganchos mal utilizados, eso debe solucionarse primero.163~164La forma saludable de probarlo:165~1661. actualizar `eslint-plugin-react-hooks`;1672. corregir violaciones reales;1683. habilitarlo en un área controlada;1694. medir el tiempo de construcción y el comportamiento;1705. elimine la memorización manual sólo cuando ya no sea necesaria.171~172El objetivo no es borrar todos los `useMemo`. El objetivo es dejar de escribir memorización preventiva porque tenemos miedo de renderizar.173~174## `proxy.ts` y el límite de la red175~176El antiguo `middleware.ts` se convierte en `proxy.ts`. Es un cambio de nombre, pero tiene sentido: ese archivo se encuentra en el límite de la solicitud, no es un middleware genérico tradicional de estilo backend.177~178```typescript179// proxy.ts180import { NextRequest, NextResponse } from 'next/server';181~182export default function proxy(request: NextRequest) {183 const isLoggedIn = Boolean(request.cookies.get('session'));184~185 if (!isLoggedIn && request.nextUrl.pathname.startsWith('/dashboard')) {186 return NextResponse.redirect(new URL('/login', request.url));187 }188~189 return NextResponse.next();190}191```192~193La regla aquí es simple: mantenlo pequeño. Redirección, enrutamiento de autenticación, encabezados, reescrituras esenciales. Si empieza a parecer un segundo backend, probablemente esté haciendo demasiado.194~195## Cómo realmente migraría196~197No activaría todas las funciones a la vez. Yo haría esto:198~1991. Actualizo Next, React y React DOM;2002. Lanzo el codemod oficial;2013. Arreglé cambios importantes en `params`, `searchParams`, `cookies()`, `headers()` y `draftMode()`;2024. Migro `middleware.ts` a `proxy.ts`;2035. Reviso compilaciones y páginas críticas;2046. Habilito Componentes de caché en una sección donde el caché actualmente crea fricción;2057. Defino convenciones para etiquetas e invalidación;2068. Intento React Compiler por separado;2079. Comparación de métricas antes y después.208~209La buena migración no es la que utiliza todas las funciones nuevas. Es lo que hace que el comportamiento de la aplicación sea más legible.210~211## ¿Qué cambia en la forma de pensar?212~213Lo más útil de Next.js 16 es que te obliga a nombrar mejor las intenciones. Una función no es simplemente "obtener el producto de la base de datos". Es "obtener el producto, puedo almacenarlo en caché durante horas, lo invalido con esta etiqueta". Un componente no es simplemente "renderizar la página". Es "este es el caparazón rápido, esta pieza es personal, esto viene en streaming".214~215Al principio parece más trabajo. Entonces se convierte en una forma de calma. Las decisiones de desempeño ya no están ocultas en una combinación de valores predeterminados, heurísticas y memoria tribal. Están en el código.216~217## Fuentes útiles218~219- [Notas de la versión de Next.js 16](https://nextjs.org/blog/next-16)220- [Componentes de caché: documentos de Next.js](https://nextjs.org/docs/app/getting-started/cache-components)221- [usar caché - Next.js docs](https://nextjs.org/docs/app/api-reference/directives/use-cache)222- [Reaccionar compilador v1.0](https://react.dev/blog/2025/10/07/react-compiler-1)223~
NORMAL · nextjs-16-cache-components.md [readonly]223 lines · :q to close