Video Summary and Transcription
Esta Charla explora la experiencia de enviar componentes de servidor en producción y destaca los beneficios y desafíos de usar Componentes de Servidor en aplicaciones Next.js. La Charla discute la implementación de UploadThing y el uso de AppRouter para un uso seguro en producción. Se adentra en la implementación de diferentes diseños, obtención de datos y centralización de código para mejorar el rendimiento. La Charla también cubre el uso de componentes de servidor para la optimización del rendimiento y el manejo de latencia. Además, explora el uso de Edge y Lambda para la pre-renderización parcial y los desafíos enfrentados con el rendimiento y la hidratación de webpack. En general, la Charla enfatiza los beneficios y desafíos de trabajar con Componentes de Servidor en aplicaciones Next.js.
1. Introducción a UploadThing y Componentes del Servidor
Hola, soy Theo, un ingeniero que lanzó el proyecto UploadThing. Vamos a adentrarnos en la experiencia de enviar componentes del servidor en producción durante más de un año. Aprendimos mucho, tanto cosas geniales como terribles. Un agradecimiento especial a React Summit por esta oportunidad.
Probablemente me conozcas de YouTube, pero aunque no lo creas, también soy un ingeniero. Sí, envío code. De hecho, envié un proyecto realmente grande alrededor de esta misma época el año pasado. Es posible que hayas oído hablar de él. Se llama UploadThing. Es una cosa para subir archivos. Esto es. Así es como se ve. En mi opinión, es la mejor manera de agregar la carga de archivos a tu proyecto, pero eso no es de lo que estamos aquí para hablar hoy, aunque me encantaría, porque necesitamos adentrarnos en los componentes del servidor. Específicamente, ¿cómo ha sido enviar componentes del servidor en producción durante un poco más de un año? Sí, nos comprometimos completamente con AppRouter. Incluso nos comprometimos completamente con Edge en este proyecto. Aprendimos muchas cosas geniales, muchas cosas que funcionaron muy bien y por supuesto, muchas cosas que funcionaron terriblemente. Quiero profundizar en todos estos detalles porque normalmente cuando lo haría en un video, nadie lo vería. Pero como los tengo a todos ustedes como rehenes en esta conferencia, podemos hablar de estas cosas con mucho más detalle de lo que normalmente puedo. ¿No es emocionante? Un agradecimiento enorme a la cumbre de React por permitirme hacer esto, grabar el video, enviarlo con anticipación porque esto es un gran alivio del estrés. Ojalá pudiera haber estado allí en persona, pero espero que todo salga bien el próximo año.
2. UploadThing Deployment and AppRouter
Sumergámonos en UploadThing, una aplicación tradicional de Next.js implementada con AppRouter. Dividimos los paquetes y la infraestructura en repositorios separados para abrir el código fuente de los paquetes. Hemos aprendido mucho a través de este proceso y mostraremos algunas de las cosas geniales que hemos descubierto. Aunque la mayoría de nuestras cosas aún están en Vercel, estamos probando otras opciones de implementación para ciertos componentes. También discutiremos AppRouter, que es crucial para utilizar los Componentes del Servidor de manera segura en producción. Si necesitas más información, echa un vistazo a mis videos de YouTube sobre el enrutador de páginas en Next.js.
De todos modos, sumergámonos. Tengo una lista completa de lo bueno, lo malo y lo feo y quiero repasar todas estas partes.
De acuerdo, lo primero es lo primero, ¿qué es esta aplicación y cómo se implementa? Bueno, UploadThing es una aplicación bastante tradicional de Next.js que utiliza AppRouter. Podemos echar un vistazo en nuestra cuenta de Vercel PENG, ir a UploadThing y no verás nada demasiado emocionante aquí. Estábamos usando FileThing como nuestro nombre original, pero nos quedamos con UploadThing. Está conectado a un repositorio de GitHub. Se implementa automáticamente. Sin embargo, el repositorio de GitHub es donde las cosas comienzan a ser un poco más interesantes, porque el repositorio de GitHub es un monorepo.
Originalmente, teníamos los paquetes para UploadThing, así como la infraestructura, todo en un solo repositorio. Los separamos porque queríamos abrir el código fuente de todos los paquetes. Así que si tienes curiosidad sobre cómo funcionan los paquetes, no hablaremos mucho de eso aquí. Todo eso es de código abierto. Siéntete libre de investigar. Así es como funciona nuestra infraestructura y hemos aprendido mucho a través de esto y no puedo esperar para mostrar algunas de las cosas geniales que hemos aprendido.
Pero como dije, bastante tradicional, lo conectas a Vercel y se implementa automáticamente una vez que le dices en qué de estos paquetes está el código, que está en infrared/servidor de UploadThing. Como puedes ver aquí, hemos estado probando otros lugares para implementar más y más de nuestra infraestructura. Definitivamente hablaremos de eso a medida que avancemos, porque aunque la mayoría de nuestras cosas siguen siendo completamente Vercel, estamos utilizando otras piezas para otras cosas como la gestión de nuestros buckets de S3, la gestión de nuestra capa de redirección encima de esos. Eso lo hemos estado haciendo por separado. En primer lugar, vale la pena reconocer que esto no se trata solo de los Componentes del Servidor, porque también estamos utilizando AppRouter, que francamente es la única forma real de utilizar los Componentes del Servidor de manera segura en producción en este momento. Sí, estamos en AppRouter más Componentes del Servidor. Tendremos que hablar mucho sobre AppRouter para que esta sea una conversación justa. Si no estás familiarizado, Next.js reemplazó su enrutador, que era el enrutador de páginas, donde solo tenías archivos page.tsx aleatorios.
Bueno, en realidad no eran page.tsx. Cualquiera que fuera el nombre del archivo, se convertía en la URL. Así que si tenías Theo.tsx, lo que exportaras allí se convertiría en page/Theo. Ahora estamos en AppRouter, que es una forma diferente de hacer las cosas. No quiero profundizar demasiado en cómo solían funcionar las cosas. Así que si necesitas aprender sobre eso, tengo toneladas de videos en mi canal de YouTube sobre cómo funcionaba el enrutador de páginas, cómo se comparaba con esto. Espero cierto nivel de conocimiento de Next.js a medida que avanzamos.
3. Explorando Diseños y Anidamiento
Esta parte explora los aspectos tradicionales del diseño de la página de inicio en UploadThing, el diseño raíz que se aplica a cada página, y la naturaleza de anidamiento de los diseños y cómo funcionan con la URL.
Esto es más para mostrar lo que hemos aprendido, no solo cómo funcionan realmente estas cosas. Aquí tenemos la página de inicio, page.tsx. Observa que el tiempo de ejecución es edge. Hablaremos de eso más adelante. No te preocupes. Pero todo esto es bastante tradicional.
Tenemos nuestro envoltorio de inicio, que es un componente envoltorio que tiene un montón de contenido que queremos en la página de inicio y páginas similares. Tenemos nuestros diseños flexibles y todo eso. Tenemos el héroe. Tenemos el llamado, que es la pequeña noticia en la página de inicio, todo cosas tradicionales. Nada demasiado interesante aquí, excepto por el hecho de que nada de esto code se envía en el JS, lo cual es realmente genial porque esto se envía solo en HTML.
Lo que vine a mostrar aquí es el diseño, porque el diseño es code que se ejecuta en cada página del sitio. Dado que este es el diseño raíz, esto se aplicará en cada subpágina. Entonces, si quieres que tu subpágina haga algo diferente, necesitas enrutarlo por separado de este diseño o encontrar formas de anular estos comportamientos porque esto es lo que envuelve todo. Por lo general, las cosas que colocas aquí son cosas como tu proveedor de autenticación, tu componente de vista de página, tu proveedor para tu capa de autenticación, no autenticación, esto es analítica. Ese es mi proveedor post hoc. Aquí tienes el proveedor trpc para la pequeña parte de trpc que estamos usando en la aplicación, que también hablaremos. Tenemos el tostador aquí para gestionar las publicaciones cuando haces clic en algo y aparece un pequeño mensaje emergente. Todas las cosas que deseas en cada página. Esto es como la raíz de la aplicación, lo que rodea a todos tus componentes en el app TSX.
Todos hemos tenido eso. Pero este es solo el diseño raíz. Como mencioné antes, hay capas en esto. Entonces, si vamos al panel de control, verás que tenemos estas tres secciones. La que nos importa ahora mismo es la ID de la aplicación. Y aquí tenemos un diseño, que es el diseño del panel de control, luego se monta la navegación lateral, luego se montan los hijos. Este diseño se aplica debajo del anterior. Y si una de estas páginas tuviera su propio diseño, también se aplicaría. La naturaleza de anidamiento de los diseños y cómo se aplica a la URL. Ahí está la verdadera magia aquí.
4. Diferentes Diseños y Obtención de Datos
Este patrón de utilizar dos diseños diferentes nos permite tener diferentes comportamientos y componentes para diferentes partes del panel de control. Al utilizar una sintaxis especial para extraer valores de la ruta, podemos pasar parámetros y utilizarlos en varias partes de nuestro código. La obtención de datos se facilita mediante la comprobación de versiones desactualizadas y realizando llamadas a la base de datos. La función 'return null' asegura que solo se monten los componentes relevantes, simplificando el código y mejorando el rendimiento.
Y honestamente, este patrón ha sido genial. Hay bordes como los que ves aquí. Aunque teníamos todas estas cosas múltiples bajo slash dashboard, queríamos que el dashboard como la ruta fuera diferente de un ID dado dentro de él. Porque cuando vamos a mi dashboard, queríamos que todas estas partes diferentes se comportaran de manera diferente desde el nivel superior aquí. Queríamos mostrar el selector para que pudieras cambiar de organización y otras cosas con un nav superior diferente aquí. Queríamos que las cosas dentro fueran diferentes. Queríamos este componente aquí, queríamos otros comportamientos adicionales y cosas por el estilo.
Para hacer eso, tuvimos que tener dos diseños diferentes. Y necesitamos asegurarnos de que este no se aplique cuando queremos que este se aplique. Para hacer eso, tienes que usar la sintaxis especial para extraer cosas de la ruta. Entonces, los paréntesis son invisibles. Significa que esto es efectivamente si solo hicieras la barra diagonal y luego el que tiene los corchetes, ese es un valor que puedes obtener ahora. Entonces, si voy a page TSX aquí, puedes ver que tengo app ID como una propiedad que se pasa como un parámetro. Y ahora que tengo esto que se pasa aquí, puedo usarlo para cosas. En este caso, tengo un enlace para ello. Estoy seguro de que hay muchos otros lugares donde usamos esto más. Oh, app outdated SDK es props que app ID.
Y así de fácil es hacer la obtención de datos, lo cual honestamente, podemos hacer ahora el hecho de que si necesito acceder rápidamente a algo como una cosa de database, es tan simple que es hilarante. Entonces, esta code app tiene outdated SDK, lo que hace es comprobar qué versión del SDK utilizaste en tu carga de archivos más reciente para ver si está desactualizada. Dado que este es un tutorial antiguo, lo que estábamos usando obviamente está desactualizado. Por lo tanto, recomienda que actualicemos a la última versión. Y lo hace ejecutando esta función donde obtenemos la versión del SDK para la app, que es solo una llamada a la database, por cierto, luego obtenemos la última versión de UploadThing que se verifica en el registro de npm, incluso podríamos almacenar en caché esto de manera bastante trivial para hacer las cosas aún más rápidas.
Y luego, si la versión está desactualizada, lo cual sabemos después de comparar estas para asegurarnos de que la nueva versión que tienes está dentro de la más reciente. De lo contrario, devolvemos esta advertencia, incluso tenemos el simple return null aquí, esto es solo tu componente habitual. Pero dado que esto se ejecuta en el servidor, podemos llamar directamente a cosas del servidor como esta. Y si esto devuelve null, entonces simplemente no obtienes nada de vuelta en el lado del cliente, no hay ninguna penalización aquí en absoluto. Este JavaScript no explota en el cliente, el usuario obtiene esto que efectivamente no existe, lo cual es mágico. Es tan genial que podrías escribir teóricamente un código súper complejo aquí que solo se monta si ese componente lo devuelve, y simplemente puedes ejecutar lógica para asegurarte de que no lo haga. Estos patrones de obtención de datos no han quedado obsoletos, todavía se siente mágico cada vez que puedes simplemente esperar y obtener información de la app, que es solo esta llamada a la database. Es tan genial que en un componente, simplemente puedes esperar data.
Y si no tienes lo que quieres, simplemente devuelve null, eso hace que todo sea mucho más simple. Y hemos descubierto que esta simplicidad escala absolutamente, no escala en el sentido de que nunca volverás a tener complejidad.
5. Centralizando el Código y Optimizando el Rendimiento
Al centralizar nuestro código y usar drizzle para la seguridad de tipos, podemos actualizar y mantener nuestra aplicación fácilmente. La lógica compleja y las utilidades se pueden implementar en el servidor sin afectar el tamaño del paquete del cliente. También hemos optimizado la carga de patrones desde un CDN descargándolos e incrustándolos en el marcado.
Pero escala en el sentido de que es una ganancia de 10 veces. Y esa ganancia de 10 veces permite que las cosas se sientan mucho menos complejas a medida que continúas construyendo sobre ello. Es posible que hayas notado que obtener información de la aplicación no es una llamada directa a la database en nuestro componente, nadie realmente recomienda hacer eso. Es una demostración muy buena para mostrar una llamada SQL dentro de un componente con el onclick porque muestra lo estrechamente relacionadas que están estas cosas, pero nadie debería hacer eso.
En cambio, lo que estamos haciendo aquí es escribir realmente estamos usando drizzle en su mayor parte, pero incluso hacemos algunas consultas SQL aquí para ciertas cosas relacionadas con los certificados SSL. Todo esto está aquí simplemente porque nos gusta tenerlo centralizado en un solo lugar, lo que hace que sea mucho menos probable que dupliquemos la lógica de formas que no tienen sentido. Y de esta manera, si usamos obtener información de la aplicación en 15 lugares, podemos actualizarla en este único lugar y afectará a todo lo demás. Y como estamos usando drizzle, que tiene un retorno seguro de tipos, inmediatamente obtendremos un aviso si elimino apt aquí desde aquí, inmediatamente obtenemos un error de tipo, porque algo aquí espera conocer el nivel, no tuve que guardar esto, puedo simplemente ni siquiera guardar. Entonces, si elimino esto, no he guardado y estamos obteniendo un error de tipo allí. Es tan genial que desde el backend hasta la interfaz de usuario, tenemos una seguridad de tipos completa que nos evita cometer errores tontos. Y eso no desaparece sin importar cuán grande sea tu base de código. Y esta se ha vuelto muy grande con el tiempo. Y podemos tener lógica realmente compleja para, como bytes, lo legible para humanos, bueno, este no es super complejo, pero entiendes la idea, esta es una utilidad que se ejecuta en nuestro servidor. Y siempre y cuando no lo importes en el cliente, no tendrás que lidiar con ningún aumento potencial del tamaño del paquete o algo así.
Como resultado, creo que eso es increíble. Una cosa más sobre el aumento del tamaño del paquete porque hay un par de estos momentos mágicos Oh, mierda que suceden una vez que profundizas en los componentes del servidor. Y uno de mis favoritos con mucho, parecerá tan simple y tonto, pero realmente hizo que estas cosas encajen para mí, ver todos estos patrones en esta página, donde tenemos un pequeño patrón elegante para todas las aplicaciones diferentes y un color de fondo diferente. Todos ellos provienen de un solo archivo. Mira este archivo generar patrón, este archivo es bastante grande. Revelé todos estos patrones. Y cada uno de ellos es una línea para el propio patrón. Pero son enormes. Este archivo es de megabytes. ¿Te preguntas de dónde vinieron todos esos patrones? patrones de héroe. Genial. Literalmente los descargamos todos y los colocamos en un solo archivo porque no queríamos que aparecieran porque se cargan desde un CDN y luego entran. Queríamos que estuvieran en el marcado. Entonces, si miras aquí, verás en el marcado real, tenemos el SVG, tengo que profundizar un poco más para verlo. Pero entiendes la idea aquí. Es el estilo incrustado aquí data imagen SVG, sí, entiendes la idea.
6. Componentes del Servidor y Optimización del Rendimiento
Incrustamos patrones de fondo en el HTML, evitando la necesidad de una lógica compleja de carga de SVG. Los RSC eliminan la necesidad de empaquetar SVG y brindan una experiencia de usuario fluida. Suspense permite la carga no bloqueante de código y datos, mejorando el rendimiento. Combinar Suspense con componentes del servidor simplifica la lógica y reduce la necesidad de límites de suspense.
Lo incrustamos como un elemento de estilo de fondo real. Por lo tanto, está en el HTML, por lo que no hay aparición repentina. Obviamente, no quiero enviar este archivo gigante, aunque la lógica aquí es realmente simple: patrón de fondo, color de primer plano y opacidad. Realizamos algunas sustituciones del relleno para que sea el nuevo color de primer plano, luego intercambiamos estos para que sea válido como un patrón data en lugar de solo ser un SVG. Entiendes la idea. Esto fue lo que Mark arregló hace mucho tiempo, pero obtener un número aleatorio es muy simple. Tenemos la semilla, que es el ID de tu aplicación, y generamos el patrón correcto que queremos usar y lo devolvemos. Este código nunca toca el cliente en absoluto. Incluso podríamos afirmar eso si realmente quisiéramos poniendo import server only en la parte superior. Y ahora, si intentamos importar esto en algún lugar donde no deberíamos, obtendremos un error que este código se está importando en algún lugar que está en el cliente. Eso significa que todo este montón de cosas que hemos metido aquí, estos enormes SVG, tenemos literalmente más de 100, no afecta el rendimiento ni el tamaño del paquete en absoluto. Esta forma realmente tonta y simple de hacer las cosas es totalmente segura y funciona bien. Ahora, aquí es donde la magia realmente comenzó a encajar para mí, porque fue menos que los RSC son una alternativa a las API o simplemente la cosa que usas en el servidor. Entonces, los RSC hacen que muchas de las cosas que tuvimos que construir alrededor de estos locos patrones simplemente desaparezcan. Piensa en todo el tiempo que hemos pasado haciendo lógica loca de carga de SVG y tratando de empaquetar SVG de la mejor manera posible para permitir que nuestros usuarios tengan una buena experiencia. ¿Y si no tuviéramos que hacer nada de eso? ¿Y si eso simplemente fuera cómo funciona? ¿No sería genial? Eso es lo que esto ha estado haciendo por mí en tantos lugares diferentes.
Una cosa más que me encanta antes de comenzar a adentrarnos en las cosas dudosas, quiero encontrar un buen ejemplo de mi patrón favorito, suspense. Suspense es increíble porque te permite hacer ciertas cosas sin bloquear las otras cosas. Entonces, en este caso, suspense evita que el código que administra las vistas de página posteriores, que es nuestro proveedor de análisis, un saludo a post hoc, han sido geniales para trabajar con ellos. Y este proveedor de análisis simplemente no es algo en lo que queramos bloquear la carga de la página porque tiene que cargar JS, tiene que cargar lógica y tiene que hacer algunas cosas. Si podemos transmitir ese resultado cuando esté listo sin hacer que tarde más, pero al final hacemos que todo lo demás tarde menos, ¿por qué no hacerlo?
Ese es un ejemplo bastante simple, pero también tenemos ejemplos más complejos donde tenemos cosas como miembros de la organización, que es un componente que tiene que obtener datos de varios lugares. ¿No sería agradable no tener que bloquear toda la página en eso? O algo como la página de archivos. Entonces, esta es la tabla de archivos y la tabla de archivos tiene que obtener un montón de datos. Entonces, la tabla de archivos no tener que bloquear la carga del resto de la página significa que cuando navegues a ella en el panel de control, se cargará de inmediato. Y si solo quieres obtener algunos datos de tu base de datos, tarda un poco más que todo lo demás. Pon eso en un componente con un suspense encima, se carga de inmediato. En general, el suspense ha sido una experiencia muy agradable, y estoy muy contento con ello. Cuando lo combinas con algunas de las habilidades locas que proporcionan los componentes del servidor, como la capacidad de pasar un componente del servidor como una propiedad a un componente del cliente para que esté anidado de esa manera. Esto ha sido muy, muy agradable de trabajar y nos ha permitido simplificar una cantidad significativa de nuestra lógica. Dicho esto, es posible que notes que a pesar del tamaño de esta base de código, no tenemos demasiados límites de suspense.
7. Componentes del Servidor y Manejo de Latencia
Hemos descubierto que colocar un buen suspense en el límite de carga más pesado y luego dejar que todo lo demás espere su camino al infierno nos ha permitido tener aplicaciones que se sienten realmente rápidas en general. El rendimiento del sitio es increíble, especialmente si te encuentras en entornos de internet no muy buenos. Su capacidad para manejar la latencia es una de las cosas más geniales. El número de viajes de ida y vuelta ha disminuido mucho y eso muestra la increíble experiencia que tienen los usuarios en esta aplicación. Sin embargo, tuvimos que hacer algunos compromisos para lograrlo.
Eso se debe a que tendemos a colocarlos en el nivel superior con algo simple y obvio, como un diseño. Y luego dejamos que las páginas carguen todo de una vez. De lo contrario, si usas el suspense en exceso, terminarás en el mismo infierno de la animación de carga del que hemos estado tratando de salir. Hemos descubierto que colocar un buen suspense en el límite de carga más pesado y luego dejar que todo lo demás espere su camino al infierno nos ha permitido tener aplicaciones que se sienten realmente rápidas en general. Y Dios, se sienten rápidas.
Como mostré antes con cosas como el SVG que simplemente no se carga. El rendimiento del sitio es increíble, especialmente si te encuentras en entornos de internet no muy buenos. Recuerdo que estaba en otra conferencia, GraphQL Conf, y alguien estaba hablando de cómo las aplicaciones modernas de React y Next.js simplemente no son tan eficientes como algo en Rails o PHP. Y cuando estaba en el avión, intentaba navegar por una aplicación aleatoria de Next.js. Spoiler, esa aplicación aleatoria de Next era el panel de control de Vercel. Y lo estaba pasando realmente mal porque cuando estás en el aire con una conexión Wi-Fi realmente inestable y una cantidad increíble de latencia, la experiencia no era buena. Y honestamente, estoy bastante de acuerdo. El panel de control de Vercel es uno de los mejores ejemplos de una aplicación de Next.js que no es muy buena de usar. Han estado progresando desde entonces, pero es difícil.
Dicho esto, te desafío a que hagas dos aplicaciones y las cargues y juegues con ellas cuando estés en Wi-Fi de avión, porque te prometo que volarán. Ahora mismo estoy en Wi-Fi realmente bueno y aún se mueve tan rápido, pero te prometo que lo ralentizaré. Puedo reducir el ancho de banda si quiero, pero un ancho de banda más bajo no va a representar adecuadamente el caos de lo agradable que se siente esto. Su capacidad para manejar la latencia es una de las cosas más geniales. La razón de eso son muchas pequeñas cosas. No hay una sola cosa grande. Es el hecho de que simplemente puedes cargar la carga útil desde el servidor en lugar de tener que navegar, luego cargar el nuevo JS, luego obtener del servidor el nuevo JSON, luego renderizar ese nuevo JSON, darte cuenta de que necesitas otras cosas, ir a buscar eso. El número de viajes de ida y vuelta ha disminuido mucho y esa reducción de los viajes de ida y vuelta no ha cambiado con el tiempo. Todavía tenemos tres o menos viajes de ida y vuelta para obtener todo el contenido de la página. Eso es increíble y muestra la increíble experiencia que tienen los usuarios en esta aplicación. Sin embargo, tuvimos que hacer algunos compromisos para lograrlo. Y aquí es donde empezamos a adentrarnos en lo negativo por un momento. Por mucho que me encanten los componentes del servidor, es posible que hayas notado algunos lugares en toda la aplicación donde tenemos esta pequeña exportación constante llamada runtime igual a edge bit. Hay una razón para eso. Cuando te pasas a los componentes del servidor inicialmente, todas tus páginas web tienen que pasar por el servidor de React y Next.js.
8. Edge, Lambda y Pre-renderización Parcial
Para que los usuarios obtengan una respuesta de tu servidor en lugar de la CDN, hay un retraso ya que el servidor necesita iniciarse, procesar la solicitud y generar una respuesta HTML. Los inicios en frío en Lambda pueden agregar un retraso significativo al tiempo de carga de la página de inicio. Para abordar esto, utilizamos Edge y fuimos la primera aplicación en producción en implementar el enrutador de aplicaciones y los componentes del servidor en el edge. Aunque hubo puntos problemáticos al tener dos entornos de ejecución, colocarlo en el edge mejoró los tiempos de renderización inicial. Sin embargo, estamos emocionados de adoptar la pre-renderización parcial en el futuro para mejorar aún más el rendimiento y eliminar la necesidad de Edge y Lambda al mismo tiempo.
Para que eso funcione, el usuario ya no obtiene una respuesta de la CDN. Ahora debe obtener la respuesta de tu servidor. Entonces, si envío una solicitud a un servidor de Next.js, no obtengo una respuesta de la caché de la CDN de inmediato. Debo esperar a que el servidor se inicie, reconozca mi solicitud, procese la solicitud, genere una respuesta HTML y finalmente me la envíe, lo cual lleva mucho más tiempo que obtener una respuesta de la caché de una CDN.
Dado que esto también estaba en Lambda, estábamos experimentando inicios en frío todo el tiempo y los inicios en frío, especialmente si estás utilizando algo complejo como Drizzle, no son tan malos, pero Prisma podría agregar segundos, literalmente segundos, de 1.0 a 2.0 segundos, 2000 milisegundos a tus inicios en frío. Miserable. No queríamos lidiar con eso porque no queríamos que la página de inicio tardara hasta dos segundos en cargarse. Queríamos que la página de inicio fuera instantánea. Como no teníamos el beneficio de algunas cosas nuevas de las que hablaremos en un momento, nos enfocamos en Edge y fuimos la primera aplicación en producción que implementó el enrutador de aplicaciones y los componentes del servidor en el edge. Y lo sé porque hicieron muchos cambios para nosotros a último momento durante nuestro lanzamiento. Eso generó muchos puntos problemáticos. Tener dos entornos de ejecución en tu aplicación, con expectativas completamente diferentes de cómo funciona el mundo, resultó en muchas cosas molestas, como no usar CUID y usar UUID en su lugar, porque no pude hacer que el paquete CUID 2 funcionara en el edge y en Lambda normal al mismo tiempo. Pero al menos, al colocarlo en el edge, nuestros tiempos de renderización inicial fueron más rápidos. Y todavía lo son. Si vas a nuestra página de inicio, aún no está obteniendo una respuesta de la CDN hasta el día de hoy. Cerraré sesión para mostrar correctamente la página de inicio. Estoy actualizando con Comando + Mayús + R. Es inmediato. No puedes hacer que cargue realmente. Es tan rápido porque puede generar una respuesta en el edge que es súper simple en casi ningún tiempo.
Pero ¿no sería genial si fuera literalmente ningún tiempo? ¿Y si, utilizando las mismas cosas de suspense que acabo de mostrar, pudiéramos cargar el shell instantáneamente desde una CDN y transmitir el resto? Lo que quería decir es que quiero simplemente almacenar en caché el primer fragmento que se envía a través del flujo, ponerlo en una CDN, enviarlo de inmediato y luego enviar el resto más tarde. Llevo pidiendo esto desde hace más de un año, posiblemente incluso dos en este punto. Afortunadamente, Vercel escuchó. Durante NextConf del año pasado, anunciaron la pre-renderización parcial, que es literalmente esto. Te permite tomar la parte estática que no cambia para ningún otro usuario. Eso es solo el shell o lo que decidas. Y siempre que haya un límite de suspense antes de que haya los primeros datos específicos del usuario, simplemente puede almacenar en caché esa capa superior. Aún no lo hemos configurado aquí porque estamos satisfechos con el rendimiento de nuestra solución actual. Y eso implica todo tipo de cambios que tendremos que hacer porque construimos muchas cosas personalizadas aquí. Pero en un futuro muy cercano, estoy emocionado de adoptar la pre-renderización parcial y finalmente poder prescindir de Edge porque ejecutar tanto Edge como Lambda al mismo tiempo ha sido complicado, especialmente para nosotros, ya que estamos utilizando el SDK de AWS de manera bastante intensiva.
9. Rendimiento, TRPC y Webpack
Si se utilizan componentes del servidor sin pre-renderización parcial, es posible que el rendimiento no sea satisfactorio. Es esencial encontrar formas de renderizar la mayor parte del contenido estáticamente o utilizar enrutamiento y tiempo de ejecución en Edge o almacenamiento en caché a través de CDN. Tuvimos problemas con las acciones del servidor y volvimos a TRPC, que ha sido confiable y valioso. TRPC es una capa de RPC segura para TypeScript que permite llamar directamente a las funciones del backend desde el frontend. A pesar de adoptar componentes del servidor, TRPC aún se puede utilizar de manera intercambiable. Sin embargo, reemplazar todo no es fácil. Nos enfrentamos a desafíos con el rendimiento de Webpack y aún estamos esperando la reescritura prometida por TRPC.
Espero que estés entendiendo la idea aquí. No estábamos satisfechos con el rendimiento de los componentes del servidor sin pre-renderización parcial y recurrimos a Edge por esa razón. Si estás utilizando esto y esperas una página de inicio rápida, busca alguna forma de renderizar la mayor parte estáticamente. Si no lo haces, el rendimiento será malo y te sentirás mal. Esto es algo necesario, ya sea enrutamiento y tiempo de ejecución en Edge o almacenamiento en caché a través de CDN. De lo contrario, verás esa temida página en blanco, lo cual no es cómo deberían comportarse nuestras aplicaciones.
Hablando de cosas aleatorias que podrías haber notado mientras navegaba, mencioné brevemente TRPC. No estábamos satisfechos con las acciones del servidor cuando intentamos consumirlas. Tenían muchos efectos secundarios extraños e inesperados relacionados con la seguridad, la mayoría de los cuales se han solucionado desde entonces. Fue molesto implementarlos, administrarlos y enlazarlos. En general, se han vuelto mucho más simples, pero no queríamos aferrarnos demasiado a eso desde el principio. Así que rápidamente eliminamos las cosas que habíamos construido alrededor de las acciones del servidor y volvimos a nuestro querido TRPC, que ha seguido comportándose de maravilla para nosotros. Si no conoces TRPC, es la capa de RPC segura para TypeScript. Te permite escribir una función para el backend y luego llamarla prácticamente directamente desde tu frontend. Es realmente útil. Forma parte del stack T3. Si no has oído hablar de eso, mira mis videos de YouTube. Si tienes curiosidad, TRPC ha seguido siendo increíblemente útil y valioso para nosotros. Todavía lo utilizamos mucho para el comportamiento de nuestras acciones, así como para algunas de nuestras cargas de datos con suspense, como nuestras tablas de desplazamiento infinito. Ha sido genial trabajar con él. Todavía amo TRPC. Sigue siendo mi opción confiable y de respaldo. No tienes que sentir que pierdes algo como TRPC solo porque estás adoptando componentes del servidor. Puedes usarlos juntos e intercambiablemente. Totalmente bien. Sin embargo, no todo se puede reemplazar tan fácilmente.
Una de las cosas más dolorosas con las que hemos estado lidiando es el rendimiento del servidor de desarrollo al usar Webpack. Hemos estado atascados con Webpack durante un tiempo porque hemos estado esperando pacientemente a TRPC, la maravillosa reescritura prometida de Webpack y Rust que se ha mencionado y predicho en el pasado durante mucho tiempo. TRPC aún no está listo. Se está acercando.
10. TRPC, Webpack y Hydration
TRPC está mejorando la experiencia de desarrollo para Next, especialmente en máquinas antiguas. El rendimiento de Webpack ha sido un desafío, causando problemas en el rendimiento del servidor de desarrollo. La hidratación ha sido un punto doloroso, especialmente al crear componentes en el paquete instalado. Para resolver esto, desarrollamos el complemento NextSSR para renderizar el componente del cliente tanto en el servidor como en el cliente y evitar errores de hidratación.
Está muy cerca. Esperemos que incluso la próxima semana veamos que TRPC sea completamente compatible al 100%, entre comillas. Listo para el desarrollo. Aún no hemos llegado allí. Y especialmente en personas con máquinas antiguas, la experiencia de desarrollo para Next fue miserable por un tiempo. El enrutador de aplicaciones simplemente no es particularmente rápido para generar nuevas páginas. Ahora, con TRPC, es mucho mejor.
El hecho de que Vercel haya esperado a TRPC cuando el rendimiento de Webpack era tan atroz como lo era, habría sido bueno hacer más correcciones y encontrar formas de mejorarlo en el ínterin. Han hecho algo, pero no lo suficiente. Y definitivamente hemos estado sufriendo con un rendimiento atroz del servidor de desarrollo hasta el punto en que, si no tenemos un MacBook más nuevo, ni siquiera un M1 parece suficiente en este momento. Estoy usando mis Macs M2 y aquí se siente bastante bien. Pero Dios, la única vez que intenté ejecutar ese código en mi máquina con Windows, fue doloroso. Me di cuenta de que me estoy quedando sin tiempo. Así que voy a apresurarme a través de algunos puntos dolorosos más. Uno, la hidratación. Siempre es molesto. Y hubo un par de lugares donde la hidratación fue particularmente difícil. No aquí, pero específicamente cuando queríamos crear componentes en el paquete que instalas. Cuando usas UploadThing, obtienes un componente de botón de carga que ejecuta el código UploadThing para cargar un archivo que el usuario está subiendo, pero valida y autentica todo en tu servidor, no en nuestros servidores, en los tuyos. Porque creemos que es muy importante que autentiques a cada usuario antes de que tengan permiso para hacer una carga. También, en tu servidor, eliges qué tipos de contenido están permitidos. ¿Pueden subir imágenes? ¿Qué tamaño de imagen pueden subir? ¿Cuántas pueden subir? Y mostramos todo eso en un pequeño botón debajo porque es muy agradable ver imágenes de hasta cuatro megabytes debajo de tu botón de carga. Queríamos que esto no tuviera un estado de carga y apareciera. Cada vez que intentamos usar cosas como el gancho Use que pasaría una promesa, simplemente se renderizaría infinitamente y se rompería terriblemente en el cliente.
Así que necesitábamos una forma de hacer un componente del cliente que pueda renderizarse en el servidor y en el cliente y tener el estado incorporado. Así que creamos lo que llamamos el complemento NextSSR. Y lo que hace es que literalmente vuelca todos los datos de configuración de UploadThing como un blob JSON dentro de una etiqueta de script en tu página para asegurarnos de tener los datos que necesitamos tanto en el servidor como en el cliente para renderizar esto correctamente y evitar errores de hidratación. En realidad funciona muy bien, pero el hecho de que tuvimos que idear un truco ingenioso como este para que funcione es molesto.
11. Building Full Stack Packages with React and Next
Construir paquetes de pila completa con React y Next puede ser desafiante. Existe la necesidad de una mejor interfaz entre el cliente y el servidor. Empresas como nosotros han puesto mucho esfuerzo en hacer que el código funcione de manera ideal en ambos entornos. Aunque hay puntos dolorosos, estos son superados por los beneficios de trabajar con Componentes del Servidor.
Y ese es el otro punto doloroso más grande que he tenido en general. Y esto es más un punto de dolor, más una súplica. Esto necesita una mejor forma de interfaz entre el cliente y el servidor. Ha sido increíblemente doloroso. No al construir aplicaciones. Al construir aplicaciones, es literalmente mágico. Pero una vez que comienzas a construir paquetes para ello, donde deseas entrelazar el servidor y el cliente en un paquete que luego se consume, no funciona tan bien.
Hay muchos cambios, tanto grandes como pequeños, que podrían hacerse en el lado de React y Next para facilitar la creación de un paquete de pila completa. Pero aún estamos llegando allí. Y la cantidad de trabajo que empresas como nosotros, empresas como Payload, empresas como Clark han puesto para hacer code que funcione de manera ideal en ambos entornos ha sido realmente doloroso. Imagina si un paquete no pudiera proporcionar componentes y ganchos, y trataras de tener un paquete que usara ganchos en sus componentes o, Dios no lo quiera, componentes dentro de sus ganchos, que React simplemente levantaría las manos y diría lo siento. Así es como se siente construir paquetes que usarán react-server y react-client en este momento. Esto debería cambiar en el futuro, pero por ahora ha sido doloroso. Si quieres ver ese dolor, echa un vistazo a la cosa de carga de código abierto code. Está lleno de eso.
Dicho esto, nunca volveremos atrás. Esto es lo mejor que ha sido. Soy el más feliz que he estado escribiendo react. Y realmente recomiendo que pruebes los componentes del servidor si aún no lo has hecho. Están cambiando la forma en que pienso sobre escribir software. Obviamente no hay garantías. Y cuando inviertes en algo tan futurista, puedes terminar perdiendo y es posible que no terminemos usando esto a largo plazo. Pero por ahora, se siente genial trabajar con ello. Y si quieres aprender algo nuevo y no tienes miedo de que posiblemente no sea lo próximo grande, no puedo recomendar lo suficiente los Componentes del Servidor. Y honestamente, me sorprendería mucho si no es así como terminamos escribiendo software en el futuro. Hasta la próxima, ¡paz nerds!
Comments