Video Summary and Transcription
Builder.io es un CMS visual sin cabeza que permite arrastrar y soltar componentes de interfaz de usuario. Los Core Web Vitals son importantes para mejorar el rendimiento del sitio web. La hidratación en los frameworks afecta el rendimiento y la interacción con la aplicación. Qwik ofrece un enfoque diferente para la hidratación, eliminando la necesidad de descargar código innecesario. La función de resumibilidad de Qwik mejora el rendimiento al comenzar con HTML y evitar la reejecución del código.
1. Introducción a Builder.io y Qwik
Hola, mi nombre es Marciko Heveri, CTO en Builder.io. Trabajamos en Qwik, un nuevo tipo de framework. Builder.io es un CMS visual sin cabeza que permite arrastrar y soltar componentes de UI. También tenemos Party Town y un equipo de personas increíbles trabajando en nuestro código abierto.
Hola, mi nombre es Marciko Heveri, soy el CTO en Builder.io, y he trabajado en estas cosas llamadas Angular y Karma, y ahora estamos trabajando en Qwik. Así que hablemos de Builder.io por un momento. Builder.io es un CMS visual headless. Básicamente significa que puedes arrastrar y soltar tus componentes en tu aplicación existente que puedes instalar usando, puedes instalar Builder.io usando npm install. Ok, dejame reiniciar. Ok, vamos a empezar de nuevo desde aquí. Qwik, un enfoque sin hidratación para mejorar el rendimiento de los sitios web. Hola, mi nombre es Mischko Heveri, soy CTO en Builder.io y he trabajado en esto llamado Angular y ahora trabajo en Qwik, que es un nuevo tipo de framework del que quiero hablar hoy. Builder.io es un CMS visual headless. Headless significa que se aloja en tu sitio web existente donde puedes instalarlo usando npm install y visual significa que puedes tomar tus propios componentes y permitir que tu equipo de marketing arrastre y suelte la interfaz de usuario y construya sitios web sin necesidad de tener experiencia en programación. Otra cosa genial que hacemos se llama Party Town, y estas son algunas de las personas increíbles que trabajan en nuestro código abierto en Builder.io.
2. Mejorando el rendimiento con Core Web Vitals
Hoy quiero hablar sobre Core Web Vitals y la importancia de reducir JavaScript para mejorar el rendimiento. Muchos sitios web tienen dificultades para pasar Core Web Vitals, incluyendo Amazon. Para ilustrar, comencemos con un ejemplo simple de contador e introduzcamos gradualmente componentes más realistas. Las aplicaciones del mundo real consisten en múltiples componentes, envoltorios, acciones y actualizaciones de interfaz de usuario. Estos componentes a menudo necesitan compartir datos a través de envoltorios. A pesar de su simplicidad, incluso esta aplicación básica requiere descargar varios archivos de código al cliente.
Entonces, lo que quiero hablar hoy es sobre la velocidad, y específicamente sobre los Core Web Vitals y cómo lograr que sean lo más altos posible. Resulta que la respuesta es relativamente simple pero difícil de hacer, enviar menos JavaScript. A Google le importa mucho, y como resultado, crearon estas métricas de Core Web Vitals que rastrean y evalúan.
Pero resulta que a medida que nuestros sitios web se vuelven más populares y complicados, la cantidad de JavaScript que enviamos al cliente está aumentando lentamente a lo largo de los años, como puedes ver aquí. A medida que aumenta la cantidad de JavaScript, se vuelve más difícil pasar los Core Web Vitals. La mayoría de los sitios web tienen dificultades para pasar los Core Web Vitals, incluso Amazon, que se preocupa mucho por el rendimiento y es uno de los sitios web con mejor rendimiento. Y realmente se reduce a la cantidad de JavaScript que se envía al cliente.
Comencemos con un ejemplo muy simple. Construyamos un ejemplo de contador. Bueno, el contador probablemente se vería así. Tenemos un botón al que le das clic y se incrementa el contador. Esta es la aplicación más simple que se puede pensar. Pero no creo que sea realmente representativa de una aplicación del mundo real, porque en esta aplicación en particular tenemos nuestra mutación, que es el escucha, nuestro estado, que es el contador actual, y la representación, que es la vinculación del valor, todo dentro de un solo componente. En realidad, por lo general, la mutación, el estado y la representación se separan en componentes separados.
Veamos eso como un ejemplo más realista. O descompongamos esto. Un ejemplo de esto se vería así, donde tienes el componente del contador que está en la raíz y contiene dos componentes más pequeños, la acción y la visualización, donde el contador contiene el estado y la acción tiene el escucha, y la visualización muestra solo el contador actual. Incluso eso no es realmente muy realista, porque típicamente tenemos cosas adicionales como AppRoot, tenemos envoltorios adicionales que se utilizan para el diseño de los componentes, y así sucesivamente. Todas estas cosas adicionales hacen que sea más realista para lo que es la aplicación real. Esto es lo que se vería.
Como puedes ver, todavía tenemos un contador, pero ahora nuestra acción está envuelta en un componente adicional, al igual que nuestra visualización. La razón por la que muestro esto es porque necesitas pasar datos a través de estos envoltorios adicionales, y esto afectará la forma en que se ejecuta la aplicación. Por último, introduzcamos un elemento de acción y un ícono de visualización. La idea detrás de estas cosas es que son básicamente saltos. Son componentes que no hacen nada, pero muchas veces los tenemos en nuestra aplicación. Un ejemplo más realista de cómo se ve realmente una aplicación es una mezcla de estos componentes de la aplicación, el contador, diferentes tipos de envoltorios, finalmente un lugar donde realmente realizamos algunas acciones y un lugar donde actualizamos la interfaz de usuario. Puedes pensar en la realización de la acción como el botón de compra en una página, y puedes ver el contador como la visualización del carrito de compras actual en la interfaz de usuario. Entonces, lo que tenemos es una situación en la que la mutación y la visualización están realmente separadas entre sí, y tienen que compartir un componente raíz común, el denominador común mínimo que abarca ambos, que contiene el estado, y luego el estado se pasa a ambos lugares. Ahora, pensarías que para una aplicación tan simple, el único código que tendrías que descargar al cliente es realmente solo la acción de mutación, tal vez el estado y tal vez la visualización.
3. Comprendiendo la Hidratación en los Frameworks
La hidratación en los frameworks requiere descargar toda la aplicación y afecta la capacidad de interactuar con la app. Los frameworks necesitan oyentes, estado de la aplicación y vinculaciones de componentes para funcionar correctamente. El framework comienza en el componente raíz, lo renderiza y descarga los componentes necesarios y su información relacionada. A partir de ahí, desciende a los componentes hijos.
Pero lo que voy a hablarles es, en realidad, que tienen que descargar toda la aplicación, y tiene que ver con la forma en que funciona la hidratación. Así que realmente, esto es el mundo ideal, ¿verdad? Realmente solo debería tener que descargar estas piezas de código. Todo lo demás es realmente innecesario y estático. Pero la hidratación entra en juego, y para que el framework pueda hacer cosas, necesita tres piezas de información, ¿verdad?
Lo primero, el framework necesita saber dónde están los oyentes, porque sin oyentes, no se puede hacer clic, interactuar o hacer nada. Lo siguiente que necesitas es el estado de la aplicación. Entonces, nuevamente, piensa en el botón, que tiene el más uno para el contador. A menos que sepas cuál es el recuento actual, en nuestro ejemplo era uno, dos, tres, no sabes cuál es el siguiente valor, y por lo tanto no puedes interactuar realmente con una aplicación sin su estado. Y finalmente, la aplicación necesita saber dónde están los componentes. Más específicamente, el framework necesita saber, perdón, no la aplicación, el framework necesita saber dónde están las vinculaciones, ¿verdad? Necesita saber que si el estado de la aplicación muta, qué componentes deben volver a renderizarse para mostrar lo que está sucediendo.
Entonces, la pregunta es, ¿cómo obtiene el framework esta información? Y así, nuevamente, necesitamos los oyentes, los oyentes necesitan mutar el estado, y se necesita el estado para actualizar los componentes. ¿Entonces, cómo se obtiene esto? Bueno, se comienza con el componente raíz, con la raíz de la aplicación, y el framework, esto es algo que se puede pensar como el método principal, ¿verdad? Y se comienza a renderizar en esta ubicación, y luego el framework va y descarga el contador, y ejecuta el contador, y aprende que el contador contiene el estado. Y luego comienza a descender a los hijos.
4. El Impacto de la Hidratación en el Rendimiento
La hidratación obliga a ejecutar cada componente en la página, lo que conduce a la descarga de código innecesario. Los frameworks solo pueden cargar de forma diferida el código que no está en la página. La hidratación requiere que todo el código esté presente, lo que afecta el rendimiento.
Entonces va al contenedor de acción, allí, nada interesante, y luego entra en el componente de acción, y allí aprende que el componente de acción tiene un oyente. Y así recopila esa información. Ahora también ve el elemento de acción. Y desafortunadamente, el framework no sabe si hay un botón allí o no, realmente no tiene más opción que descender y visitar esa cosa en particular también. Así que va y visita el elemento de acción. Y luego finalmente vuelve al contador, toma la segunda rama, y va al contenedor de visualización, va al renderizado de la visualización y finalmente va al icono de visualización.
Y así, la hidratación básicamente te obliga a ejecutar cada componente que está actualmente en la página, ¿verdad? No hay forma de evitar esto, o saltarlo, o hacer cualquier otro comportamiento. Y así, aunque para que nuestra aplicación funcione, solo necesitamos realmente el contador, la acción y la visualización, la hidratación básicamente nos obligó a descargar todo el código. Ahora algunos de ustedes podrían decir, como, hey, pero una vez que la aplicación está en ejecución, ya sabes, los frameworks pueden ser inteligentes y saben cómo saltar y podar estos árboles y no descender en cosas que no importan. Y todo eso es cierto, pero eso solo puede suceder después de que el framework haya ejecutado completamente todo lo que está en la página. Lo otro que también hay que señalar es que los frameworks realmente solo pueden cargar de forma diferida el código que no está actualmente en la página, ¿verdad? Entonces no hay una forma sencilla de cargar de forma diferida el contenedor de acción, por ejemplo, porque el framework durante la hidratación necesita visitar, necesita pasar por el contenedor de acción para llegar a la acción. Y así lo que quiero mostrarles es que la hidratación realmente requiere que todo este código esté presente en el cliente, y pueden ver cómo se están marcando todas las casillas aquí. Y nuevamente, como para una aplicación grande, por supuesto, ya saben, nueve componentes no es algo grande, pero en el mundo real donde tienes cientos y cientos de componentes, todo suma. Y si cada componente necesita estar presente y disponible, tiene implicaciones en el rendimiento.
5. Un Enfoque Diferente para la Hidratación
Existen diferentes alternativas de hidratación, pero exploremos un enfoque completamente diferente que evita por completo la hidratación. La hidratación debe comenzar en el componente raíz y visitar cada componente individual. Todos los frameworks, incluido Quick, necesitan información sobre los listeners, el estado de la aplicación y los componentes que deben volver a renderizarse.
Ahora, existen muchas alternativas de hidratación, desde la arquitectura de islas hasta las hidrataciones parciales y progresivas. No tengo tiempo para profundizar en esto, y todas estas son diferentes formas de mejorar la situación y realmente lo hacen en cierta medida. Pero lo que realmente quiero mostrarte es una forma completamente diferente de hacerlo donde evitamos por completo la hidratación.
Ahora, antes de hacer eso, quiero señalar que la hidratación debe hacerse en orden, lo que significa que una hidratación, por definición, debe comenzar en el componente raíz y recorrer todos los hijos, ¿verdad? No es posible comenzar la hidratación en medio de tu aplicación. Incluso si sabes que un componente en particular es estático en la raíz y no lo necesitas, no hay forma de omitirlo. Realmente tienes que comenzar desde la raíz y visitar cada componente individual. Bien, volvamos a esta diapositiva, y creo que es una diapositiva importante. Ahora, dije que todos los frameworks realmente necesitan tener estas tres piezas de información, ¿verdad? Cada framework necesita saber dónde están los listeners, cada framework necesita saber cuál es el estado de la aplicación y cuáles son los componentes que deben volver a renderizarse si cambia el estado de la aplicación. Y esto es cierto para, como dije, todos los frameworks, incluido Quick, del que hablaremos a continuación.
6. Comparación entre Hidratación y Resumibilidad
En el mundo de la hidratación, volvemos a ejecutar todos los componentes para obtener información sobre los listeners, el estado de la aplicación y los componentes. La resumibilidad envía información sobre las vinculaciones, el estado y los listeners en el HTML, lo que permite que la aplicación esté lista para funcionar sin volver a ejecutar código en el cliente. Esto elimina la necesidad de descargar y ejecutar código innecesario, lo que proporciona ahorros y mejora el rendimiento.
Pero la forma en que obtenemos esta información realmente marca una gran diferencia. Y como dije, en el mundo de la hidratación, la forma en que obtenemos esta información es volviendo a ejecutar todo lo que ves en la página, ¿verdad? Entonces volvemos a ejecutar todos los componentes, y mientras volvemos a ejecutar estos componentes, vamos aprendiendo dónde están los listeners, cuál es el estado de la aplicación y qué componentes hay, y cuándo los componentes necesitan volver a renderizarse.
Entonces intentemos hacer algo diferente. La hidratación funciona, como dije, de esta manera particular, donde comenzamos con un renderizado del lado del servidor o SSG, comenzamos ejecutando el código de la aplicación. Y mientras ejecutamos el código de la aplicación, aprendemos dónde están las vinculaciones, cuál es el estado de la aplicación y dónde están los listeners. Luego serializamos el HTML del resultado. Y luego, cuando vamos al navegador, el navegador nos muestra nuestra aplicación. Pero el framework, para que sea interactivo, necesita recuperar esa información sobre los listeners. Y así, el framework comienza a ejecutar nuevamente la aplicación. Y mientras ejecuta la aplicación, aprende dónde están las vinculaciones, dónde está el estado y dónde están los listeners. Y cuando todo esto se vuelve a ejecutar, tu aplicación está lista para interactuar.
Ahora, la resumibilidad funciona de manera ligeramente diferente. Así como antes con la hidratación en el servidor, no hay diferencia. Comienzas ejecutando el código, mientras ejecutas tu código, aprendes dónde están las vinculaciones, cuál es el estado y dónde están los listeners en la aplicación. Pero aquí es donde las cosas son diferentes. Obviamente, envías el HTML. Pero además del HTML, realmente envías información sobre las vinculaciones, el estado actual de la aplicación y dónde están los listeners. Y al incluir esa información en el HTML, resulta que cuando vas al navegador, cuando un navegador se despierta y muestra tu aplicación, el framework ya sabe dónde están los listeners , cuál es el estado y dónde están las vinculaciones. Y como resultado, no necesita volver a ejecutar ningún código en el cliente al iniciar. La aplicación está lista para funcionar. Y esta es la gran diferencia entre la hidratación y la resumibilidad. Y debido a que podemos omitir todo este código, obtienes beneficios de varias maneras. En primer lugar, no estás ejecutando el código, por lo que tienes ahorros allí. Y en segundo lugar, porque no estás ejecutando el código, resulta que probablemente ni siquiera necesitas descargar la mayor parte del código. Es posible que se deba descargar parte del código, pero no todo. El truco de la resumibilidad es que sabemos cómo transferir el estado desde el servidor donde lo teníamos al cliente. La hidratación reconstruye el estado en el servidor, pero luego lo desecha. No lo serializa en HTML, lo que significa que se pierde en el proceso de ir al cliente. El punto principal de la resumibilidad es que no solo incluimos el HTML, la aplicación renderizada , sino que también incluimos información sobre dónde están los enlaces, cuál es el estado del sistema y dónde están las vinculaciones. Como resultado, podemos omitir por completo esta parte aquí, que es la reejecución.
7. Comparación entre Hidratación y Qwik
La hidratación requiere descargar y ejecutar todo el código JavaScript, seguido de la reconciliación. La resumibilidad, por otro lado, comienza con HTML y no requiere pasos adicionales. Qwik funciona de esta manera, conociendo la ubicación de los listeners y el estado del sistema, lo que permite obtener beneficios de rendimiento. Con Qwik, solo se descarga el código necesario, lo que resulta en significativamente menos código en el cliente en comparación con la hidratación.
La forma en que se ve en un gráfico de línea de tiempo es que cuando navegas a un sitio web, el primer fragmento aquí es el HTML. Esto es lo que necesitas descargar antes de ver la aplicación. En este punto, ves la aplicación, pero no puedes interactuar con ella. La hidratación ahora requiere que descargues todo el código asociado con JavaScript, luego debes ejecutar todo el código asociado con JavaScript, y como paso final, hay una reconciliación. Aquí es donde vuelves al DOM y verificas que tenga todos los listeners y todas las piezas. Por lo general, los listeners son la parte que debes volver a adjuntar en esta fase en particular.
Ahora, la resumibilidad también comienza con HTML, como se muestra aquí, pero no hay pasos adicionales. Simplemente estás listo en este punto, porque el framework sabe dónde están los listeners . Sabe cuál es el estado del sistema y toda la información adicional. Puedes continuar desde donde lo dejaste. Aquí es donde obtienes los beneficios de rendimiento. Así es como funciona Qwik, y esto es lo que es fundamentalmente diferente acerca de Qwik. Veamos Qwik, cómo funcionaría esto. En un servidor, ejecutas todos estos componentes como antes, y luego descargas el HTML asociado al cliente. Este HTML realmente contiene ahora las cajas rojas. El HTML contiene información sobre el estado, contiene información de que aquí es donde ocurre la mutación cuando haces clic en ella, y aquí está la renderización que debe actualizar la interfaz de usuario.
Específicamente aquí, en realidad no sabemos cuál es la mutación. Solo sabemos que algún código debe ejecutarse si interactúas. El código se carga cuando es necesario. Como resultado, cuando haces clic en un botón en Qwik, el único código que se descarga es el listener de Qwik. Ese es necesario. Debes descargar ese código y ejecutarlo. El segundo código que puede o no descargarse es el de la visualización. Dependiendo de si la visualización tiene cambios estructurales o no, Qwik puede evitar incluso descargar la visualización. Si tiene cambios estructurales, es posible que se vea obligado a descargar también la visualización. En cada caso, puedes ver que la cantidad de código que se necesita en el cliente es significativamente menor que en la hidratación. Más específicamente, lo que quería mostrarte es que con la hidratación, había una casilla de verificación en todas partes aquí. Cada una de esas columnas debía ejecutarse antes de poder hacer cualquier cosa. Mientras que con Qwik, no hay nada que se deba hacer de manera anticipada, y solo algunas cosas suceden de manera perezosa. Ahí es donde provienen las ganancias de rendimiento.
8. Understanding Lazy Loading and Memoization
Cargamos menos código y evitamos duplicar el trabajo a través de la memorización. Los frameworks solo pueden cargar de forma perezosa y hacer la memorización después de descargar y ejecutar todos los componentes en una página. Qwik Insights observa cómo se ejecuta la aplicación y recopila estadísticas anónimas para determinar la probabilidad de ejecutar ciertas funciones.
La forma en que funciona esto es que cargamos menos código, hacemos menos trabajo y no duplicamos el trabajo, que es la memorización. Vas a decir, sí, pero mi framework sabe cómo cargar de forma perezosa el código, y mi framework sabe cómo hacer la memorización, y todas estas cosas. Voy a decir, absolutamente, eso es cierto. Sin embargo, tu framework solo puede hacerlo después de haber descargado todos los componentes que están actualmente en una página y haber ejecutado todos los componentes en una página. La memorización, por ejemplo, para no descender en ciertos subárboles, solo funciona después de haber hecho la hidratación, lo que significa que después de haber descendido al menos una vez con éxito en todo, luego en ejecuciones posteriores, no tienes que descender en esos árboles.
Algo importante a saber aquí es el precargado. Sé lo que estás pensando. Estás pensando, si Qwik es perezoso y si interactúas con un botón en particular y de repente no tienes ese fragmento de código, el usuario tendrá que esperar a que el código se descargue a través de la red. Quiero mostrarte la parte importante que hace Qwik. Se llama Qwik Insights. Lo que hace Qwik Insights es observar cómo se ejecuta la aplicación en realidad. En primer lugar, Qwik Insights es un servicio. No tienes que usarlo. Es opcional. Pero si lo haces, recopila toda esta información. Esta información es completamente anónima y básicamente recopila estadísticas que dicen, hey, si ejecutas esta función, que se muestra como un SHA particular aquí, entonces tienes una cierta probabilidad de que también vayas a ejecutar esta otra función. En este caso particular, hay un 19% de probabilidad de que si ejecutas esta función en particular, se necesite esta otra función. Lo mostramos en lo que llamamos una matriz de correlación. Puedes ver que la mayor parte de la matriz aquí está vacía, lo que significa que en su mayor parte, si ejecutas un fragmento de código aquí, no tiene implicaciones sobre la ejecución de ningún otro código en cualquier parte del sistema. Solo hay muy pocos lugares que tienen una correlación entre la ejecución de un fragmento de código con otro fragmento de código. Luego, Kwik puede recopilar esta información en diferentes histogramas que muestran cómo se necesitan estas funciones cuando el usuario interactúa con el sistema. Con base en esta información, Kwik puede hacer dos cosas. Uno, Kwik puede hacer agrupamiento, lo que significa que puede decir, hey, lo que he observado es que siempre que los usuarios interactúan con un sistema, siempre necesitan la función A y la función B juntas. Entonces, ¿por qué no ponemos la función A y la función B juntas en el mismo fragmento de código? La segunda cosa a observar es que a medida que navegamos por diferentes rutas, el Insights de Kwik aprende en qué orden se necesitan realmente estas funciones. Por lo tanto, podemos decirle al service worker que precargue todas estas cosas. Entonces, lo que realmente tenemos es un service worker cuyo trabajo es precargar todo el código en caso de que el usuario interactúe con él. Ahora, si volvemos atrás unas diapositivas, lo que sabemos es que no hay un caso de uso en el que el Kwik realmente tenga que descargar toda la aplicación porque muchos de los componentes son estáticos y nunca se vuelven a representar en el cliente. Entonces, lo que quieres aprender, lo que Kwik Insights quiere aprender es qué fragmentos de código de todos ellos realmente tienen que descargarse en el cliente. Y luego tengo que asegurarme de que se descarguen en el orden correcto, lo que significa que el botón en el que es más probable que hagas clic se descarga primero, y el botón en el que es menos probable que hagas clic se descarga más tarde. Y el código que está asociado con este botón probablemente debido al agrupamiento debería ir en el mismo fragmento de descarga. Todo esto significa que como desarrollador, puedes simplemente construir aplicaciones en Kwik y no tienes que pensar en la optimización. No tienes que pensar en cómo dividir tu aplicación en pequeños fragmentos. ¿Cómo cargo de forma perezosa el código? ¿Cómo me aseguro de no descargar código innecesario? Esto es diferente en un mundo típico donde desarrollas algún código, tu performance disminuye, luego tomas un descanso. Te enfocas en solucionar el performance y vuelves a desarrollar y tu performance disminuye un poco y luego tienes que volver y descubrir cómo optimizar y así sucesivamente. Con Kwik, puedes concentrarte en construir una aplicación y no tienes que pensar en el performance y todas estas optimizaciones como la carga perezosa, la ejecución perezosa y la memorización porque es simplemente cómo funciona Kwik de forma predeterminada. Entonces, Kwik te brinda código de carga reducida, hace menos trabajo y no duplica el trabajo a través de la memorización. De todos modos, espero que lo hayas disfrutado y nos hablamos pronto.
Comments