00:00:00(Música animada) - Bueno, hola a todos.
00:00:06Mi nombre es Aurora.
00:00:07Soy desarrolladora web de Noruega.
00:00:09Trabajo como consultora en Crane Consulting y también estoy desarrollando activamente con el enrutador de aplicaciones de Next.js en mi proyecto de consultoría actual.
00:00:16Hoy les enseñaré patrones sobre composición,
00:00:18almacenamiento en caché y arquitectura en Next.js moderno que les ayudarán a garantizar la escalabilidad y el rendimiento.
00:00:24Permítanme primero repasar el concepto más fundamental para esta charla: la renderización estática y dinámica.
00:00:30Los encontramos ambos en el enrutador de aplicaciones de Next.js.
00:00:33La renderización estática nos permite construir sitios web más rápidos porque el contenido pre-renderizado puede ser almacenado en caché y distribuido globalmente,
00:00:39asegurando que los usuarios puedan acceder a él más rápidamente.
00:00:42Por ejemplo, el sitio web de Next.js Conf.
00:00:46La renderización estática reduce la carga del servidor porque el contenido no tiene que ser generado para cada solicitud de usuario.
00:00:51El contenido pre-renderizado también es más fácil de indexar para los rastreadores de motores de búsqueda,
00:00:56ya que el contenido ya está disponible al cargar la página.
00:00:58La renderización dinámica,
00:00:59por otro lado,
00:01:00permite que nuestra aplicación muestre datos en tiempo real o actualizados con frecuencia.
00:01:05También nos permite servir contenido personalizado,
00:01:07como paneles de control y perfiles de usuario.
00:01:09Por ejemplo, el panel de control de Vercel.
00:01:12Con la renderización dinámica,
00:01:13podemos acceder a información que solo se puede conocer en el momento de la solicitud.
00:01:16En este caso,
00:01:17qué usuario está accediendo a su panel de control,
00:01:19que soy yo.
00:01:20Hay ciertas API que pueden hacer que una página se renderice dinámicamente.
00:01:25El uso de las props `params` y `search params` que se pasan a las páginas o sus hooks equivalentes provocará la renderización dinámica.
00:01:32Sin embargo,
00:01:32con `params`,
00:01:33podemos predefinir un conjunto de páginas pre-renderizadas usando `static params` genéricos,
00:01:36y también podemos almacenar en caché las páginas a medida que son generadas por los usuarios.
00:01:40Además,
00:01:41leer las cookies y los encabezados de las solicitudes entrantes hará que la página opte por la renderización dinámica.
00:01:46A diferencia de `params`,
00:01:47sin embargo,
00:01:48intentar almacenar en caché o pre-renderizar algo usando encabezados o cookies generará errores durante la compilación porque esa información no se puede conocer de antemano.
00:01:56Por último,
00:01:56usar `fetch` con una configuración de caché de datos `no store` también forzará la renderización dinámica.
00:02:00Así que estas son algunas,
00:02:02hay algunas API más que pueden causar renderización dinámica,
00:02:04pero estas son las que encontramos con más frecuencia.
00:02:06En versiones anteriores de Next,
00:02:08una página se renderizaba como completamente estática o completamente dinámica.
00:02:13Una sola API dinámica en una página hará que toda la página opte por la renderización dinámica.
00:02:17Por ejemplo,
00:02:18hacer una simple verificación de autenticación para el valor de una cookie.
00:02:20Al utilizar componentes de servidor de React con `Suspense`,
00:02:23podemos transmitir contenido dinámico como un banner de bienvenida personalizado o recomendaciones a medida que estén listos y proporcionar solo fallbacks con `Suspense` mientras mostramos contenido estático como un boletín.
00:02:34Sin embargo,
00:02:34una vez que agregamos múltiples componentes asíncronos en una página dinámica,
00:02:38como un producto destacado,
00:02:40estos también se ejecutarían en el momento de la solicitud,
00:02:43aunque no dependieran de API dinámicas.
00:02:45Así que para evitar bloquear la carga inicial de la página,
00:02:48suspenderíamos y transmitiríamos esos componentes también,
00:02:51haciendo trabajo extra,
00:02:52creando esqueletos y preocupándonos por cosas como el cambio de diseño.
00:02:56Sin embargo,
00:02:57las páginas suelen ser una mezcla de contenido estático y dinámico.
00:03:01Por ejemplo,
00:03:01una aplicación de comercio electrónico que depende de la información del usuario mientras que aún contiene en su mayoría datos estáticos.
00:03:07Verse obligado a elegir entre ellos,
00:03:09entre estático o dinámico,
00:03:11causa mucho procesamiento redundante en el servidor sobre contenido que nunca o muy rara vez cambia y no es óptimo para el rendimiento.
00:03:19Así que para resolver este problema,
00:03:21en la Next.js Conf del año pasado,
00:03:23se anunció la directiva `use cache`.
00:03:26Y este año,
00:03:26como vimos en la presentación principal,
00:03:28está disponible en Next.js 16.
00:03:30Así que con `use cache`,
00:03:31las páginas ya no se verán obligadas a una renderización estática o dinámica.
00:03:36Pueden ser ambas.
00:03:37Y Next.js ya no tiene que adivinar qué es una página basándose en si accede a cosas como `params`.
00:03:43Todo es dinámico por defecto y `use cache` nos permite optar explícitamente por el almacenamiento en caché.
00:03:47`Use cache` permite el almacenamiento en caché componible.
00:03:51Podemos marcar una página,
00:03:53un componente de React o una función como cacheable.
00:03:55Aquí,
00:03:56en realidad podemos almacenar en caché el componente de productos destacados porque no necesita la solicitud y el procesamiento y no utiliza API dinámicas.
00:04:03Y estos segmentos de caché pueden ser pre-renderizados e incluidos como parte del shell estático con renderización parcial,
00:04:09lo que significa que los productos destacados ahora están disponibles al cargar la página y no necesitan ser transmitidos.
00:04:14Ahora que tenemos este importante conocimiento de fondo,
00:04:18hagamos una demostración.
00:04:19Una mejora de una base de código con problemas comunes frecuentemente encontrados en aplicaciones Next.js.
00:04:24Estos incluyen el `prop drilling` profundo,
00:04:25lo que dificulta mantener y refactorizar características,
00:04:28JavaScript redundante del lado del cliente y componentes grandes con múltiples responsabilidades,
00:04:31y la falta de renderización estática,
00:04:33lo que lleva a costos adicionales del servidor y un rendimiento degradado.
00:04:36Así que sí, empecemos.
00:04:37Y denme un segundo aquí.
00:04:50Muy bien, genial.
00:04:54Así que esta es una aplicación muy simple.
00:04:56Está inspirada en una plataforma de comercio electrónico.
00:04:59Y permítanme hacer una demostración inicial aquí.
00:05:01Así que puedo cargar esta página.
00:05:03Tengo contenido como este producto destacado.
00:05:06Tengo categorías destacadas, diferentes datos de productos.
00:05:09También está esta página 'Explorar todo' aquí donde puedo ver todos los productos de la plataforma y paginar entre ellos.
00:05:20Luego tenemos esta página 'Acerca de' aquí,
00:05:22que es simplemente estática.
00:05:24También puedo iniciar sesión como usuario.
00:05:27Y eso me conectará a mi usuario.
00:05:30Y también obtener contenido personalizado en mi panel de control aquí.
00:05:33Como por ejemplo,
00:05:34productos recomendados o estos descuentos personalizados aquí.
00:05:38Así que noten aquí, hay una mezcla bastante buena.
00:05:42Oh, también una página más que olvidé mostrarles.
00:05:45La página del producto, la más importante.
00:05:47También aquí,
00:05:48podemos ver información del producto y luego guardarla si queremos para nuestro usuario.
00:05:52Así que noten que hay una mezcla bastante buena de contenido estático y dinámico en esta aplicación debido a todas nuestras características dependientes del usuario.
00:05:59Echemos también un vistazo al código, que estaría aquí.
00:06:05Así que estoy usando el enrutador de aplicaciones aquí,
00:06:07por supuesto,
00:06:07en Next.js 16.
00:06:08Tengo todas mis diferentes páginas,
00:06:09como la página 'Acerca de',
00:06:11la página 'Todo',
00:06:12nuestra página de producto.
00:06:13También estoy usando `feature slicing` aquí para mantener limpia mi carpeta de la aplicación.
00:06:17Tengo diferentes componentes y consultas que se comunican con mi base de datos con Prisma.
00:06:23Así que sí, y a propósito ralenticé todo esto.
00:06:25Por eso tenemos esta etapa de carga tan larga,
00:06:28solo para que podamos ver más fácilmente lo que está sucediendo.
00:06:31Así que los problemas comunes en los que queríamos trabajar aquí y que realmente tenemos en esta aplicación eran el `prop drilling`,
00:06:37lo que dificulta mantener y refactorizar características,
00:06:39el exceso de JavaScript del lado del cliente y la falta de renderización estática que lleva a costos adicionales del servidor y un rendimiento degradado.
00:06:47Así que el objetivo de la demostración es básicamente mejorar esta aplicación con algunos patrones inteligentes sobre composición,
00:06:53almacenamiento en caché y arquitectura para solucionar esas características comunes y hacerla más rápida,
00:06:58más escalable y más fácil de mantener.
00:07:01Así que empecemos con eso.
00:07:02El primer problema que queremos solucionar en realidad está relacionado con el `prop drilling`.
00:07:05Y eso estaría aquí en la página.
00:07:10Noten aquí mismo,
00:07:11tengo esta variable `logged in` en la parte superior.
00:07:15Y pueden ver que la estoy pasando a un par de componentes.
00:07:17En realidad se ha pasado a través de múltiples niveles a este banner personal.
00:07:20Así que esto va a dificultar la reutilización de cosas aquí porque siempre tenemos esta dependencia de `logged in` para nuestro banner de bienvenida.
00:07:28Así que con los componentes de servidor,
00:07:30la mejor práctica sería realmente empujar la obtención de datos hacia los componentes que lo están usando y resolver promesas más profundamente en el árbol.
00:07:37Y para que esto se autentique,
00:07:39siempre y cuando esto esté usando `fetch` o algo como `React cache`,
00:07:42podemos duplicar múltiples llamadas de esto,
00:07:45y podemos reutilizarlo donde queramos dentro de nuestros componentes.
00:07:48Así que estaría totalmente bien reutilizarlo.
00:07:51Así que ahora podemos mover esto a la sección personalizada aquí.
00:07:54Y ya no vamos a necesitar esta prop.
00:07:57Y simplemente ponerlo directamente... ups... aquí.
00:08:01Y ya no vamos a necesitar pasar esto.
00:08:04Y como ahora estamos moviendo esta llamada asíncrona a la sección personalizada,
00:08:07ya no estamos bloqueando la página.
00:08:09Podemos seguir adelante y suspender esto con un simple `Suspense` aquí.
00:08:13Y no vamos a necesitar este `fallback`.
00:08:16En cuanto al banner de bienvenida,
00:08:19supongo que haremos lo mismo.
00:08:22Pero intentar obtener la variable o valor `logged in` aquí,
00:08:26no funciona,
00:08:27¿verdad?
00:08:27Porque este es un componente de cliente.
00:08:29Así que necesitamos resolver esto de una manera diferente.
00:08:30Y vamos a usar un patrón bastante inteligente aquí para resolver esto.
00:08:33En realidad vamos a ir al `layout` y envolver todo aquí con un proveedor de autenticación.
00:08:39Así que voy a poner esto alrededor de toda mi aplicación aquí y obtener esta variable `logged in` aquí.
00:08:45Y definitivamente no quiero bloquear todo mi `root layout`.
00:08:48Vamos a quitar el `await` aquí.
00:08:50Y simplemente pasar esto como una promesa a este proveedor de autenticación.
00:08:55Y esto puede simplemente contener esa promesa.
00:08:57Puede estar ahí esperando hasta que estemos listos para leerla.
00:09:01Así que ahora tenemos esto configurado.
00:09:03Eso significa que en realidad podemos seguir adelante y nos desharemos de esta prop,
00:09:08primero que nada.
00:09:09Y nos desharemos de este que se pasa al banner personal.
00:09:12Y nos desharemos del `prop drilling` también aquí o de la firma.
00:09:16Y ahora podemos usar este proveedor de autenticación para obtener este valor `logged in` localmente dentro del banner personal con `use auth` con ese proveedor que acabamos de crear.
00:09:26Y leerlo con `use`.
00:09:28Así que esto en realidad funcionará de una manera en la que necesitamos suspender esto mientras se resuelve.
00:09:33Así que ahora simplemente co-localicé esa pequeña obtención de datos dentro del banner personal.
00:09:37Y no tengo que pasar esas props.
00:09:40Y mientras esto se resuelve,
00:09:41vamos a suspender este también con un `fallback`.
00:09:44Y vamos a hacer un banner general aquí para evitar cualquier cambio acumulativo extraño.
00:09:51Y finalmente, también deshacernos de este.
00:09:53Así que ahora este banner de bienvenida es componible.
00:09:58Es reutilizable.
00:09:59No tenemos ninguna prop o dependencia extraña en la página de inicio.
00:10:02Y como podemos reutilizar esto tan fácilmente,
00:10:05vamos a añadirlo también a esta página del navegador aquí,
00:10:10que estará aquí.
00:10:11Y puedo simplemente usarlo aquí sin ninguna dependencia.
00:10:15Así que a través de estos patrones,
00:10:18podemos mantener una buena arquitectura de componentes utilizando `React cache`,
00:10:24`React use`,
00:10:25y hacer nuestros componentes más utilizables y componibles.
00:10:30Muy bien.
00:10:31Abordemos el siguiente desafío común,
00:10:33que sería el JavaScript excesivo del lado del cliente y los componentes grandes con múltiples responsabilidades.
00:10:40En realidad, eso también está en la página 'Todo' aquí.
00:10:43Y de nuevo,
00:10:43tendremos que trabajar en este banner de bienvenida.
00:10:46Actualmente es un componente de cliente.
00:10:48Y la razón por la que es un componente de cliente es porque tengo este estado de 'descartado' muy simple aquí.
00:10:53Puedo simplemente hacer clic aquí.
00:10:54Es una buena interacción de interfaz de usuario.
00:10:56Eso está bien.
00:10:57Lo que no está tan bien,
00:10:58sin embargo,
00:10:59es que debido a eso,
00:10:59convierto todo este componente en un componente del lado del cliente o un componente de cliente.
00:11:04E incluso uso `swr` para la obtención de datos del lado del cliente.
00:11:07Ahora tengo esta capa de API aquí.
00:11:08Ya no tengo seguridad de tipos en mis datos.
00:11:11Sí, esto no es necesario.
00:11:12Y también estamos rompiendo la separación de preocupaciones aquí porque estamos mezclando la lógica de la interfaz de usuario con los datos.
00:11:18Así que vamos a usar otro patrón inteligente para solucionar esto.
00:11:21Se llama el patrón `donut`.
00:11:23Básicamente,
00:11:23lo que voy a hacer es extraer esto en un `wrapper` del lado del cliente.
00:11:27Así que vamos a crear un nuevo componente aquí.
00:11:29Y llamémoslo `banner container`.
00:11:32Y esto va a contener nuestra lógica interactiva con la directiva `use client`.
00:11:37Podemos crear la firma.
00:11:38Podemos pegar todo lo que teníamos antes.
00:11:42Y en lugar de usar estos banners,
00:11:44voy a insertar una prop aquí,
00:11:46que serán los `children`.
00:11:48Por eso se llama el patrón `donut`.
00:11:50Simplemente estamos haciendo esta lógica de interfaz de usuario `wrapper` alrededor del contenido renderizado por el servidor,
00:11:54o podría ser contenido renderizado por el servidor.
00:11:56Y luego,
00:11:57como ya no tenemos esta dependencia del lado del cliente,
00:11:59podemos seguir adelante y eliminar el `use client`.
00:12:01Podemos usar nuestra función asíncrona `isAuth` aquí en su lugar.
00:12:06Podemos convertir esto en un componente de servidor asíncrono.
00:12:09Incluso podemos reemplazar la obtención de datos del lado del cliente con la obtención de datos del lado del servidor.
00:12:11Así que voy a obtener los datos de descuento directamente aquí.
00:12:16Datos de descuento.
00:12:18Y simplemente utilizar nuestro modelo mental regular como antes con seguridad de tipos.
00:12:24Y eso significa que también puedo eliminar esta capa de API con la que no quiero trabajar de todos modos.
00:12:29Finalmente,
00:12:29para el `isLoading`,
00:12:30podemos simplemente exportar un nuevo banner de bienvenida aquí con nuestro `banner container` con patrón `donut` que contiene contenido renderizado por el servidor.
00:12:38Y eso significa que ya no necesitamos este `isLoading`.
00:12:40Así que básicamente refactorizamos todo esto en un componente de servidor y extraemos un punto de lógica de interfaz de usuario.
00:12:46¿Pero qué es eso?
00:12:48Parece que tengo otro error.
00:12:51Así que esto es en realidad debido a `Motion`.
00:12:53Estoy usando `Motion`.
00:12:54Es una biblioteca de animación realmente genial,
00:12:57pero requiere la directiva `useClient`.
00:12:59Y de nuevo,
00:12:59no tenemos que hacer que esto sea `useClient` solo para la animación.
00:13:03Podemos crear,
00:13:04de nuevo,
00:13:04un `wrapper` con patrón `donut` y simplemente extraer `wrappers` para estas animaciones.
00:13:10Y eso significa que no tenemos que convertir nada aquí en el lado del cliente.
00:13:14Y probablemente me falta algo aquí abajo.
00:13:17Sí.
00:13:18Ahí vamos.
00:13:21Así que ahora todo aquí ha sido convertido a servidor.
00:13:23Tenemos la misma interacción.
00:13:24Todavía tenemos nuestra lógica interactiva aquí,
00:13:27pero ahora tenemos esta única forma de obtener datos.
00:13:29Y tenemos mucho menos JS del lado del cliente.
00:13:31En realidad,
00:13:32estoy usando este patrón `donut` yo misma para este `UI boundary helper`,
00:13:41que se ve así.
00:13:42¿Ven eso?
00:13:43Así que esto muestra, de nuevo, a qué me refiero, ¿verdad?
00:13:45Con el patrón `donut`,
00:13:46tenemos este componente de cliente alrededor de un componente de servidor.
00:13:49También marqué muchos de mis otros componentes con este `UI helper` aquí.
00:13:53También aquí, tengo más componentes de servidor.
00:13:56Vamos a mejorarlos también,
00:13:58ya que ya somos bastante buenos en esto.
00:14:01Están en el pie de página.
00:14:04Estas categorías...
00:14:04quiero decir,
00:14:05tengo este buen componente que obtiene sus propios datos..
00:14:08Y solo quería añadir esto de `showMore`,
00:14:11por si acaso se alarga mucho.
00:14:14Y con el patrón `donut`,
00:14:15puedo simplemente envolver un componente `showMore` aquí.
00:14:20Y esto contendrá mi lógica de interfaz de usuario.
00:14:23Y se ve así, ¿verdad?
00:14:27Bastante genial.
00:14:28Y esto ahora contiene la lógica del cliente,
00:14:31permitiéndonos usar el estado.
00:14:33Estamos usando el recuento de `children` y `to array` para cortar esto.
00:14:36Y lo genial aquí es que estos dos ahora son componentes completamente componibles y reutilizables que funcionan juntos así.
00:14:42Así que esta es realmente la belleza de estos patrones que estamos aprendiendo aquí.
00:14:45Puedes usar esto para cualquier cosa.
00:14:50También lo uso para este modal aquí.
00:14:52Sí,
00:14:52solo recuerden esto la próxima vez que estén considerando añadir cualquier tipo de lógica de cliente a sus componentes de servidor.
00:14:59OK, conocemos el patrón `donut`.
00:15:01Sabemos cómo utilizarlo para crear estos componentes componibles y evitar JavaScript del lado del cliente,
00:15:07así que podemos avanzar al problema final.
00:15:10Voy a cerrar esto de nuevo.
00:15:13Así que eso sería con la falta de estrategias de renderización estática,
00:15:18¿verdad?
00:15:18Mirando mi salida de compilación,
00:15:20en realidad tengo cada página como una página dinámica aquí.
00:15:24Así que eso significa que cada vez que cargo algo aquí,
00:15:27esto se va a ejecutar para cada usuario.
00:15:29Lo siento.
00:15:30Cada usuario que acceda a esto va a obtener este estado de carga.
00:15:33Va a estar desperdiciando costos de servidor,
00:15:35empeorando el rendimiento.
00:15:37Y eso significa también que algo dentro de mis páginas está causando renderización dinámica o forzando la renderización dinámica para todas mis páginas.
00:15:45En realidad, está dentro de mi `root layout`.
00:15:49No sé si han experimentado esto.
00:15:51Está aquí.
00:15:53En mi encabezado, tengo este perfil de usuario.
00:15:57Y esto,
00:15:57por supuesto,
00:15:58está usando cookies para obtener el usuario actual,
00:16:00y eso significa que todo lo demás también se renderiza dinámicamente.
00:16:02Porque de nuevo,
00:16:03las páginas podrían ser dinámicas o estáticas,
00:16:05¿verdad?
00:16:06Este es un problema bastante común y algo que se ha resuelto antes en versiones anteriores de Next,
00:16:11así que veamos qué podríamos hacer.
00:16:13Una cosa que podríamos hacer es crear un grupo de rutas y dividir nuestra aplicación en secciones estáticas y dinámicas,
00:16:20lo que me permitiría extraer mi página 'Acerca de'.
00:16:23Podría renderizar esto estáticamente.
00:16:25Está bien para algunas aplicaciones,
00:16:27pero en mi caso,
00:16:27la página importante es la página del producto,
00:16:30y esta sigue siendo dinámica,
00:16:31así que no es realmente útil.
00:16:33¿Qué tal esta estrategia?
00:16:35Así que aquí estoy creando este parámetro de contexto de solicitud codificando un cierto estado en mi URL,
00:16:41y luego puedo usar `generate static params` para generar todas las diferentes variantes de mis páginas.
00:16:46Eso,
00:16:47en realidad,
00:16:47combinado con la obtención de datos de usuario del lado del cliente,
00:16:51me permitiría obtener esto en caché en mi página de producto.
00:16:54Definitivamente un patrón viable.
00:16:55Es recomendado por el SDK de Vercel Flags,
00:16:58llamado el patrón `precompute`,
00:17:00creo.
00:17:00Pero esto es realmente complejo,
00:17:02y tengo múltiples formas de obtener datos.
00:17:04Y en realidad,
00:17:04no quiero reescribir toda mi aplicación en esto.
00:17:07¿Y si no tuviéramos que hacer ninguna de esas soluciones?
00:17:10¿Y si hubiera una forma más sencilla?
00:17:12Bueno, la hay.
00:17:14Volvamos a nuestra aplicación de nuevo.
00:17:17Así que en realidad podemos ir a la configuración de Next y simplemente habilitar los componentes de caché.
00:17:23Oh, genial.
00:17:25OK,
00:17:25y lo que esto hace,
00:17:26como saben por la presentación principal,
00:17:28en realidad hará que todas nuestras llamadas asíncronas opten por el tiempo de solicitud o dinámico.
00:17:34Y también nos dará errores cada vez que tengamos alguna llamada asíncrona no suspendida,
00:17:39y nos dará esta directiva `use cache` que podemos usar para almacenar en caché de forma granular una página,
00:17:46una función o un componente.
00:17:48Así que sí, vamos a utilizar esto.
00:17:51Podemos empezar con la página de inicio aquí.
00:17:55Echemos un vistazo.
00:17:56Así que de nuevo,
00:17:57tengo esta mezcla de contenido estático y dinámico.
00:17:59Tengo mi banner de bienvenida para mí,
00:18:01algo para ti también para mí.
00:18:03Echemos un vistazo a eso con este `UI helper` de nuevo.
00:18:06Así que por ejemplo,
00:18:07el banner se renderiza dinámicamente con esto de aquí.
00:18:10Mientras que marqué esto como renderización híbrida porque el héroe está obteniendo esta cosa asíncrona y va bastante lento.
00:18:18Pero no depende de ningún tipo de datos de usuario o API dinámicas.
00:18:21Así que eso significa que todo lo que se renderiza de forma híbrida aquí en realidad puede ser reutilizado en diferentes solicitudes y usuarios.
00:18:27Y podemos usar la directiva `use cache` en eso.
00:18:30Así que vamos a añadir la directiva `use cache` aquí y marcar esto como cacheado.
00:18:35Y eso me permitirá...
00:18:37cada vez que recargue esta página...
00:18:41no guardé esto..
00:18:43Ahí vamos.
00:18:44No recargará esta parte porque está en caché.
00:18:47Ahora es estático, ¿verdad?
00:18:49Y también hay otras API relacionadas como la etiqueta de caché para permitirme tipar esto o validar la entrada de caché específica de forma granular o definir mi período de revalidación.
00:19:01Pero para esta demostración,
00:19:03centrémonos solo en la directiva simple.
00:19:05Ahora que tengo esta directiva `use cache`,
00:19:07en realidad puedo eliminar mi límite de `Suspense` alrededor de este héroe.
00:19:10Y eso significa...
00:19:12bueno,
00:19:12lo que esto hará es que la renderización parcial puede incluir esto en el shell pre-renderizado estáticamente para que este héroe,
00:19:20en este caso,
00:19:21sea parte de mi salida de compilación..
00:19:23Hagamos lo mismo para todo lo demás en esta página que se pueda compartir.
00:19:28Por ejemplo, tengo estas categorías destacadas aquí.
00:19:31Vamos a hacer lo mismo allí.
00:19:33Y añadir la directiva `use cache` y marcar esto como cacheado.
00:19:37Así.
00:19:39Y podemos eliminar el límite de `Suspense`.
00:19:40Ya no vamos a necesitar esto.
00:19:43Lo mismo para los productos destacados.
00:19:44Vamos a añadir `use cache` y marcar esto como cacheado.
00:19:48Ups.
00:19:50Y luego eliminar el límite de `Suspense`.
00:19:52Así que noten cuánta complejidad soy capaz de eliminar aquí.
00:19:55No tengo que preocuparme por mis esqueletos,
00:19:57mi cambio de diseño acumulativo que estaba haciendo antes.
00:20:00Y la página ya no...
00:20:01o ya no tenemos esta limitación a nivel de página de estático versus dinámico..
00:20:07Así que ahora,
00:20:08cuando cargo esta página,
00:20:10verán que todo aquí está en caché excepto por este contenido verdaderamente específico del usuario.
00:20:16Correcto.
00:20:18Así que eso es bastante genial.
00:20:19Vamos a la página 'Explorar' y hagamos lo mismo allí.
00:20:24Sí.
00:20:25Ya marqué todos mis límites aquí para que puedan entender fácilmente lo que está sucediendo.
00:20:29Y quiero al menos almacenar en caché estas categorías.
00:20:33Parece que estoy recibiendo un error, sin embargo.
00:20:37Quizás reconozcan esto.
00:20:38Así que significa que tengo una ruta de bloqueo.
00:20:40Y no estoy usando el límite de `Suspense` cuando debería hacerlo.
00:20:43Al actualizar esto, es verdad, ¿eh?
00:20:46Esto es realmente lento.
00:20:47Y está causando problemas de rendimiento y una mala experiencia de usuario.
00:20:50Así que esto es genial.
00:20:51`Use cache` o los componentes de caché me están ayudando a identificar mis rutas de bloqueo.
00:20:55Veamos en realidad qué está pasando dentro de eso.
00:20:57Así que este es el problema, ¿verdad?
00:20:59Estoy obteniendo estas categorías de nivel superior y no tengo ningún límite de `Suspense` encima.
00:21:03Básicamente, tenemos que tomar una decisión.
00:21:05O añadimos un límite de `Suspense` encima o optamos por el almacenamiento en caché.
00:21:09Hagamos lo simple primero y simplemente añadamos un `loading.tsx` aquí.
00:21:12Y vamos a añadir una página de carga aquí,
00:21:16una bonita interfaz de usuario de esqueleto.
00:21:21Eso está bastante bien.
00:21:21Resolvió el error,
00:21:22pero no tengo nada útil sucediendo en esta página mientras espero.
00:21:25Ni siquiera puedo buscar.
00:21:27Así que con los componentes de caché, dinámico es como...
00:21:30o estático versus dinámico es como una escala..
00:21:33Y depende de nosotros decidir cuánto contenido estático queremos en nuestras páginas.
00:21:37Así que vamos a mover esta página más hacia lo estático y simplemente eliminemos este `loading.tsx` de nuevo.
00:21:43Y luego utilizar los patrones que estábamos aprendiendo antes para empujar esta obtención de datos al componente y co-localizarla con la interfaz de usuario.
00:21:50Así que movemos esto a mis filtros de categoría responsivos aquí.
00:21:54Tengo dos por el diseño responsivo.
00:21:57En realidad puedo seguir adelante y simplemente añadirlo aquí.
00:22:01Ups.
00:22:03E importar esto.
00:22:05Ya no necesito este `prompt`.
00:22:06En realidad, mi componente se está volviendo más componible.
00:22:09Y en lugar de suspenderlo,
00:22:11simplemente añadamos la directiva `use cache`.
00:22:14Y eso debería ser suficiente.
00:22:16Así que noten cómo me veo obligada a pensar más sobre dónde estoy resolviendo mis promesas y en realidad mejorando mi arquitectura de componentes a través de esto.
00:22:24No necesito suspender esto.
00:22:25Esto simplemente se incluirá en el shell estático aquí.
00:22:28La lista de productos, permítanme mantener esto fresco.
00:22:35Así que puedo recargar eso cada vez.
00:22:37Mientras que las categorías en la parte inferior,
00:22:39también quiero almacenar esto en caché.
00:22:41Así que vamos a ir al pie de página.
00:22:44Y como estoy usando el patrón `donut` aquí,
00:22:46esto en realidad puede ser almacenado en caché aunque esté dentro de esta parte de la interfaz de usuario que es interactiva.
00:22:54Así que esto está totalmente bien.
00:22:55Así que ese patrón no solo fue bueno para la composición,
00:22:57sino también para el almacenamiento en caché.
00:22:58Creo que tengo un error más allí.
00:23:03Veamos qué es eso.
00:23:04Todavía tengo este error.
00:23:08Esto es en realidad debido a estos `search params`.
00:23:10`Search params`, como sabemos, es una API dinámica.
00:23:12No puedo almacenar esto en caché.
00:23:13Pero puedo resolverlo más profundamente para revelar más de mi interfaz de usuario y hacerla estática.
00:23:18Así que vamos a mover esto hacia abajo,
00:23:20pasarlo como una promesa a la lista de productos.
00:23:24Haremos que esto sea tipado como una promesa aquí, así.
00:23:30Vamos a resolverlo dentro de la lista de productos,
00:23:33usar los parámetros de búsqueda resueltos aquí y aquí.
00:23:36Y como esto está suspendido aquí, el error desaparecerá.
00:23:40Así que al recargar esto,
00:23:42lo único que se recarga aquí es solo la parte que elegí específicamente para que fuera dinámica.
00:23:48Todo lo demás puede ser almacenado en caché.
00:23:49Y eso significa que puedo interactuar con mi banner o incluso buscar porque esa parte ya ha sido pre-renderizada.
00:23:57Muy bien,
00:23:57hagamos la página final aquí,
00:24:00que es la página del producto,
00:24:02que es la más difícil y la más importante.
00:24:05Está realmente mal ahora mismo.
00:24:08Esto es súper importante para una plataforma de comercio electrónico,
00:24:10aparentemente.
00:24:11Muy bien, vamos a arreglar ese también.
00:24:15Así que aquí tengo esta página de producto.
00:24:18Empecemos a almacenar en caché solo el contenido reutilizable aquí,
00:24:21por ejemplo,
00:24:22el producto en sí.
00:24:23Y simplemente añadir `use cache` aquí y marcar esto como cacheado.
00:24:27Eso debería estar bien.
00:24:28Eso significa que podemos eliminar el límite de `Suspense` aquí.
00:24:33Muy bien,
00:24:33y esto ya no se recarga en cada solicitud aquí,
00:24:37¿verdad?
00:24:38Para los detalles del producto, hagamos lo mismo.
00:24:40Vamos a añadir `use cache`.
00:24:41Vamos a marcarlo como cacheado y ver si eso también funciona.
00:24:47No lo hizo.
00:24:48En realidad, este es un error diferente.
00:24:50Me está diciendo que estoy intentando usar API dinámicas dentro de este segmento cacheado.
00:24:54Y eso es cierto.
00:24:54Estoy usando el botón 'Guardar Producto', ¿verdad?
00:24:56Eso me permitió hacer clic y alternar el estado guardado.
00:25:00¿Así que qué creen que podemos hacer con esto?
00:25:03Podemos usar el patrón `donut` de nuevo.
00:25:06En realidad,
00:25:06también podemos insertar segmentos dinámicos en segmentos de caché.
00:25:10Así que los estamos intercalando como antes, pero con caché.
00:25:12Así que esto es bastante genial.
00:25:14Vamos a añadir los `children` aquí así.
00:25:19Y esto eliminará el error.
00:25:21Y puedo simplemente envolver esto alrededor de este segmento dinámico de mi página aquí,
00:25:26eliminar el límite de `Suspense`,
00:25:28y añadir una interfaz de usuario de marcador muy pequeña para esa única pieza dinámica de la página.
00:25:34Y veamos cómo se ve eso ahora.
00:25:40Así que noten cómo casi toda la interfaz de usuario está disponible,
00:25:43pero tengo este pequeño fragmento que es dinámico,
00:25:46y eso está bien.
00:25:47Todo lo demás sigue ahí.
00:25:48Y dejemos las reseñas dinámicas porque podríamos mantenerlas frescas.
00:25:53Todavía hay un error más.
00:25:54Vamos a abordar eso rápidamente.
00:25:56De nuevo, estos son los `params`.
00:25:58Estoy recibiendo ayuda de que necesito tomar una decisión: o añadir un `loading fallback` o almacenar esto en caché.
00:26:04Vamos a usar `generate static params` en este caso.
00:26:07Depende un poco de tu caso de uso y de tu conjunto de datos.
00:26:10Pero para este caso,
00:26:10simplemente voy a añadir un par de páginas pre-renderizadas predefinidas y luego simplemente almacenar en caché el resto a medida que son generadas por los usuarios.
00:26:17Y esto eliminará mi error aquí.
00:26:20Así que creo que en realidad he terminado con mi refactorización.
00:26:22Vamos a echar un vistazo a la versión desplegada y ver cómo se ve.
00:26:26Así que acabo de desplegar esto en Vercel.
00:26:27Y recuerden,
00:26:29a propósito ralenticé muchas obtenciones de datos aquí.
00:26:35Y aún así,
00:26:36cuando cargo esta página inicialmente,
00:26:39todo ya está disponible.
00:26:40Lo único aquí son solo esos pocos segmentos dinámicos como los descuentos y el 'para ti'.
00:26:46Lo mismo con el 'explorar todo'.
00:26:47Toda la interfaz de usuario ya está disponible.
00:26:50Y para el producto en sí, simplemente se siente instantáneo.
00:26:54Y recuerden,
00:26:55de nuevo,
00:26:55que todos estos segmentos de caché se incluirán con el shell estático con renderización parcial.
00:27:00Y puede ser pre-obtenido usando la pre-obtención mejorada en el nuevo enrutador de cliente de Next 16.
00:27:05Así que eso significa que cada navegación simplemente...
00:27:08se siente tan rápida, ¿verdad?.
00:27:09Muy bien,
00:27:10para resumir,
00:27:11con los componentes de caché,
00:27:14ya no hay estático versus dinámico.
00:27:17Y no necesitamos evitar las API dinámicas o comprometer el contenido dinámico.
00:27:28Y podemos omitir estos hacks y soluciones complejas usando múltiples estrategias de obtención de datos solo para ese...
00:27:35este acierto de caché, como les mostré..
00:27:37Así que en Next.js moderno,
00:27:39dinámico versus estático es una escala.
00:27:40Y nosotros decidimos cuánto contenido estático queremos en nuestras aplicaciones.
00:27:43Y siempre y cuando sigamos ciertos patrones,
00:27:45podemos tener un modelo mental que es performante,
00:27:48componible y escalable por defecto.
00:27:50Así que volvamos a las diapositivas.
00:27:53Así que si no estaban ya impresionados por la velocidad de eso,
00:27:55esta es la puntuación de Lighthouse.
00:27:56Así que recopilé algunos datos de campo con Vercel Speed Insights.
00:28:00Así que tenemos una puntuación de 100 en todas las páginas más importantes: la página de inicio,
00:28:04la página del producto y la lista de productos,
00:28:06a pesar de que son altamente dinámicas.
00:28:08Así que vamos a resumir finalmente los patrones que garantizarán la escalabilidad y el rendimiento en las aplicaciones Next.js y nos permitirán aprovechar las últimas innovaciones y obtener puntuaciones como esta.
00:28:18Así que,
00:28:19en primer lugar,
00:28:19podemos refinar nuestra arquitectura resolviendo promesas profundamente en el árbol de componentes y obteniendo datos localmente dentro de los componentes usando `React Cache` para evitar trabajo duplicado.
00:28:28Podemos evitar el paso excesivo de props a los componentes de cliente usando proveedores de contexto combinados con `React Use`.
00:28:35Segundo,
00:28:35podemos componer componentes de cliente de servicio usando el patrón `donut` para reducir el JavaScript del lado del cliente,
00:28:40mantener una clara separación de preocupaciones y permitir la reutilización de componentes.
00:28:43Y este patrón nos permitirá además almacenar en caché nuestros componentes de servidor compuestos más tarde.
00:28:50Y finalmente,
00:28:50podemos almacenar en caché y pre-renderizar con `use cache` ya sea por página,
00:28:53componente o función para eliminar el procesamiento redundante,
00:28:56aumentar el rendimiento y el SEO,
00:28:57y dejar que la renderización parcial renderice estáticamente estos segmentos de la aplicación.
00:29:01Y si nuestro contenido es verdaderamente dinámico,
00:29:04podemos suspenderlo con `fallbacks` de carga apropiados.
00:29:07Y recuerden que todo esto está conectado.
00:29:09Así que cuanto mejor sea tu arquitectura,
00:29:10más fácil será componer,
00:29:11y más fácil será almacenar en caché y pre-renderizar con los mejores resultados.
00:29:15Por ejemplo,
00:29:15resolver API dinámicas profundamente en el árbol te permitirá crear un shell estático pre-renderizado parcialmente más grande.
00:29:22Y con eso,
00:29:22este es el repositorio de la versión completada de la aplicación.
00:29:25Hay tantas cosas que ni siquiera mostré allí que pueden revisar.
00:29:29Y pueden escanear el código QR para encontrar mis redes sociales allí junto con el repositorio si no quieren tomar una foto y escribirlo ustedes mismos.
00:29:36Así que sí, eso es todo por mi parte.
00:29:37Gracias Next.js Conf por tenerme aquí.
00:29:39[MÚSICA]