Repensando las Estrategias de Agrupación

Rate this content
Bookmark
Damos un vistazo a diferentes desafíos y decisiones al agrupar código para aplicaciones web. Observamos cómo se resuelven comúnmente estos y por qué necesitamos repensarlos.
Tobias Koppers
Tobias Koppers
32 min
08 Dec, 2023

Comments

Sign in or register to post your comment.

Video Summary and Transcription

La charla discute sobre repensar las estrategias de agrupación, enfocándose en desafíos como el almacenamiento en caché a largo plazo y la mejora del estado de Next.js y Webpack. Explora el manejo del almacenamiento en caché inmutable y los hashes de contenido, optimizando las referencias de activos y los manifiestos de página, y abordando problemas con la navegación del lado del cliente y el almacenamiento en caché a largo plazo. La charla también cubre la eliminación de árboles y la optimización, optimizando los fragmentos de módulo y la ubicación del código, y el uso y la relación de TurboPack con Webpack. Además, toca la personalización de la configuración y los riesgos de hash, las importaciones de barril y la división de código, y los puntos de entrada y las heurísticas de fragmentación.

Available in English

1. Replanteando Estrategias de Agrupación

Short description:

Soy Tobias Cobbers, el creador de Webpack. Hoy quiero hablar sobre replanteando estrategias de agrupación, centrándome en dos desafíos en la escritura de agrupadores. El primer desafío es el almacenamiento en caché a largo plazo, aprovechando la caché del navegador para almacenar recursos entre implementaciones. El segundo desafío implica mejorar el estado actual de Next.js y Webpack. Vamos a sumergirnos en estos desafíos y explorar cómo podemos hacerlo mejor.

Gracias. Sí, en realidad estoy hablando de replanteando estrategias de agrupación hoy, y mi nombre es Tobias Cobbers. Creé Webpack hace 11 años o 12 años, y hace dos años, o hace tres años, me uní a Vessel y trabajé un poco en Next.js, mejorando Webpack para Next.js.

Ahora estoy trabajando en TurboPack e integrando Next.js con TurboPack. Mi charla es en realidad un poco más general, así que quiero hablar de algunas cosas. Quiero ver dos desafíos diferentes en la escritura de agrupadores. En realidad estamos mirando la magia en los agrupadores. Así que agarré dos temas para eso, dos desafíos que actualmente o en el futuro enfrentaré con la construcción de TurboPack. Y quiero profundizar un poco en eso porque creo que aprender esta magia de bundler puede ser importante, incluso si técnicamente no deberías enfrentarte a ella en tu trabajo diario. El bundler debería hacerlo transparente y no debería enfrentarte con todos estos desafíos. Debería resolverlo mágicamente. Pero creo que todavía es útil saberlo, y obtienes una visión profunda de eso, y puede ayudarte en algunos casos extremos.

Primero, quiero presentar estos dos desafíos, y luego entrar en el estado actual con Next.js y Webpack para eso. Y después de eso, quiero dedicar un poco de tiempo a repensar eso y cómo podemos mejorar en eso, qué podemos hacer mejor en el futuro, y qué queremos hacer en TurboPack con estos desafíos. Un pequeño descargo de responsabilidad primero, principalmente trabajo con Next.js, Webpack, y TurboPack, así que todo es desde la perspectiva de estas herramientas. Y todavía hay otras herramientas afuera, y tienen cosas similares, diferentes implementaciones. Y aunque la mayoría de las ideas no son realmente nuevas, están más inspiradas por otras herramientas y sí. Así que el primer tema es principalmente sobre el almacenamiento en caché a largo plazo, que realmente no es muy conocido por muchas personas. ¿Y qué es el almacenamiento en caché a largo plazo en absoluto? Así que el almacenamiento en caché a largo plazo significa que queremos aprovechar la caché del navegador, por lo que la memoria caché en el navegador para almacenar nuestros recursos, y especialmente entre implementaciones. Así que básicamente hay tres niveles, o tres niveles prácticos de aprovechamiento de la caché del navegador. El primero es el almacenamiento en caché máximo, donde simplemente especificas que mis recursos son válidos para dos horas, y no tienes que verificar eso de nuevo, y puedes usar la caché para dos horas. Pero en la práctica, es bastante inadecuado para nuestro caso de aplicación, porque podríamos tener una corrección de error crítica para solucionar, y queremos implementar algo, y no queremos esperar dos horas hasta que el usuario realmente obtenga una corrección de error. Así que no queremos usar eso en absoluto. Y lo que queremos usar es como el almacenamiento en caché e-tech, por ejemplo. Y el almacenamiento en caché e-tech significa básicamente que cuando el servidor responde con el recurso, envía un encabezado especial, e-tech, que generalmente contiene un hash del contenido, y luego el navegador almacena eso en su caché, y básicamente, en la caché. Y también quieres especificar tres fechas válidas, así que la próxima vez que el navegador quiera usar el recurso, simplemente hace una nueva solicitud para eso, pero incluye un encabezado especial si-no-coincide que incluye el e-tech, por lo que el hash del contenido, y luego el servidor podría, si el recurso no cambió en el ínterin, podría responder con un código de estado especial, como, no ha cambiado, puedes usar la caché, y no necesitas descargarlo de nuevo. Y eso básicamente siempre funciona, eso es genial. Pero siempre también revalida la solicitud. Así que básicamente envía una nueva solicitud, tienes que pagar el viaje de ida y vuelta, pero no tienes que pagar el costo de descarga. Así que es bueno, pero puedes hacerlo mejor.

2. Manejo de Caché Inmutable y Hashes de Contenido

Short description:

La mejor manera de manejar el almacenamiento en caché para recursos estáticos es a través del almacenamiento en caché inmutable, donde el navegador puede almacenar en caché los recursos indefinidamente. Para garantizar la consistencia, se utiliza una URL única con un hash de contenido, lo que permite realizar actualizaciones fácilmente sin romper la caché. Para lograr compilaciones deterministas, el agrupador debe generar la misma salida para la misma aplicación, al tiempo que garantiza que los pequeños cambios resulten en pequeños cambios de salida. Sin embargo, el manejo de los hashes de contenido se vuelve más complejo cuando hay referencias entre diferentes partes de la aplicación. Webpack y Next.js han avanzado en la solución de estos desafíos, pero el problema de los hashes de contenido persiste.

El mejor, creo, al menos para recursos estáticos y para ese tipo de cosas, es el almacenamiento en caché inmutable, lo que significa que envías control de caché inmutable y algunos otros encabezados, y eso significa que el navegador puede almacenarlo en caché para siempre, nunca tiene que hacer un viaje de ida y vuelta, nunca tiene que solicitarlo de nuevo, simplemente puede almacenarlo para siempre, generalmente un año o algo así.

Pero solo funciona, básicamente, si lo almacena sin revalidar para siempre, básicamente no puedes cambiar el contenido del recurso, porque si lo cambias, entonces podría ser inconsistente, y los navegadores podrían tenerlo aún en caché, no funciona.

Entonces, generalmente abordas eso haciendo que la URL de eso sea única de tal manera que nunca cambie. Entonces, generalmente la cosa es que simplemente agregas un hash de contenido a la URL, podrías haber visto eso con nombres de archivos que tienen este hash adjunto, y eso hace que la URL sea tan única que nunca cambiará y si implementas una nueva versión, simplemente obtendrá una nueva URL con un nuevo hash.

Sí, ese sería el mejor. Entonces, ¿cómo enfrentamos eso desde un nivel de agrupador? Entonces, el desafío se puede resolver con algunas técnicas diferentes. Entonces, una cosa es que queremos hacer que el bundler de tal manera que esté generando compilaciones deterministas. Entonces, una compilación debería, si compilas la misma aplicación, simplemente debería generar el mismo activo de salida para que la caché pueda ser utilizada realmente. Si generaras diferentes activos de salida, entonces no puedes usar la caché. Pero también quieres otra propiedad. Quieres esta propiedad de que incluso si haces un pequeño cambio en tu aplicación, que usualmente haces, como en cada solicitud de extracción o lo que sea, quieres una propiedad que un pequeño cambio resulte en un pequeño cambio de salida. Si solo cambias un módulo, podrías esperar que solo uno o pocos fragmentos cambien en el paquete de salida. Y sí, esa es la forma en que podemos usar generalmente nuestra caché del navegador. Ahora queremos usar esta cosa de almacenamiento en caché inmutable, por lo que solo queremos poner un hash de contenido en cada fuente o cada nombre de archivo que emitimos desde el bundler. Suena bastante fácil. Simplemente haces un hash del contenido, lo agregas al nombre del archivo. Pero se vuelve un poco complicado porque en realidad hay referencias entre las diferentes cosas en tu aplicación. Entonces, como un ejemplo, HTML hace referencia a tus fragmentos, los fragmentos se refieren entre sí, tal vez para carga asíncrona y esas cosas. Y los fragmentos también hacen referencia a los activos, como imágenes, fuentes, esas cosas. Y ahí es donde entra el problema. Entonces, sí, básicamente resolvimos estas primeras cosas con Webpack en el estado actual con Next.js. Entonces, para hacer compilaciones deterministas, simplemente tenemos cuidado al implementar eso y tratamos de evitar partes absolutas, básicamente evitamos partes absolutas. Y para hacerlo independiente de estos cambios donde clonas tu repositorio en un directorio diferente y todas esas cosas. Y eso es bastante fácil, en realidad. Y el más difícil es esta propiedad de pequeño cambio de entrada, pequeño cambio de salida, donde tienes que considerar cada algoritmo para hacerlo realmente sin tener este efecto de toda la aplicación. Como los ID de módulo, realmente no podemos numerarlos uno por uno, tenemos que... Porque si los numeras uno por uno, insertar un módulo al principio renombraría todos los modules no a la propiedad que queremos. Entonces, haciendo uso de hashes para generar ID de módulos, y también para dividir tus modules en fragmentos, tienes que hacerlo determinista de tal manera que los pequeños cambios se conviertan en pequeños cambios de salida. También es relevante para las optimizaciones, como la ofuscación y esas cosas. En general, resolvimos algunas cosas, pero veamos este problema de los hashes de contenido.

3. Optimizando las Referencias de Activos y los Manifiestos de Páginas

Short description:

Si optas por un enfoque AETH y colocas hashes de contenido y referencias de la forma habitual, los cambios en un activo pueden causar la invalidación de todo el gráfico de dependencias. Para solucionar esto, Webpack utiliza un manifiesto que contiene todos los nombres de archivos, URLs y hashes de contenido de los fragmentos. Los fragmentos ya no se referencian directamente entre sí, sino que hacen referencia al manifiesto. Esto permite que los fragmentos no relacionados permanezcan en caché mientras solo el manifiesto y los archivos HTML cambian cuando se actualiza un activo. Sin embargo, en una aplicación de varias páginas, todos los fragmentos hacen referencia al manifiesto, lo que lleva a la invalidación de todos los archivos HTML. Para solucionar esto, se puede utilizar un manifiesto por página, aislando las páginas y eliminando las dependencias de hash entre ellas. Este cambio permite páginas independientes sin dependencias de hash, beneficiando la caché a largo plazo y de compilación, pero requiere un manejo adicional para la navegación del lado del cliente.

Si simplemente optas por un enfoque AETH, y simplemente colocas hashes de contenido y todo, referencias de la forma habitual, entonces obtienes esta propiedad donde tienes un activo aquí, como una imagen, incluyes un hash de contenido, por lo que básicamente estás haciendo un hash del contenido, el nombre del archivo se basa en una imagen, ABC, con un hash. Eso significa que, porque AsyncChunk2 hace referencia en este caso, tenemos que incrustar la URL de este activo en AsyncChunk2. Y eso básicamente significa que el hash del activo se convierte en parte de AsyncChunk2. Y AsyncChunk2 también se hace un hash y obtiene un nombre, y eso básicamente sucede para todo.

Ahora lo importante, si algo cambió, como cambié este archivo de fuente para tener más subconjunto de fuentes, lo que sea, entonces este activo, por supuesto, cambia, obtiene la nueva URL, y el problema es ahora, la nueva URL necesita ser incrustada en AsyncChunk2, y eso significa que este fragmento también cambia y obtiene una nueva URL, y eso necesita ser incrustado en AsyncChunk1. Así que básicamente el problema es que el cambio se propaga por tus dependencias, tu gráfico de referencias en tu aplicación, y al final significa que todo el gráfico se invalidará solo porque cambiaste la hoja del gráfico. Y eso no es la propiedad que queremos, queremos algo más. Y Webpack tiene una solución para eso.

Entonces, lo que hacemos en Webpack es en lugar de poner referencias de fragmentos en otros fragmentos, simplemente sacamos todos los nombres de archivos, todas las URLs, todos los hashes de contenido de los fragmentos en un manifiesto, que se incrusta en el runtime de Webpack, y hacemos referencia a eso desde ahí. En nuestro gráfico se ve así, donde estás... Básicamente todos los hashes de fragmentos están en el manifiesto, por lo que los fragmentos no se referencian directamente entre sí, básicamente se están referenciando indirectamente en su lugar. Y eso cambia la propiedad donde, si cambias el activo, todavía se propaga a AsyncChunk2, pero luego deja de propagarse a todos los demás fragmentos. Básicamente solo se propaga al archivo del manifiesto que cambia, y HTML cambiará, lo cual siempre cambiará. Pero básicamente podemos mantener todos los fragmentos, los fragmentos no relacionados en caché, y eso da mucho beneficio para el hashing.

Pero todavía hay problemas con eso. Un ejemplo en una aplicación de varias páginas, donde si tienes muchos, quizás miles de archivos HTML, y quieres navegar del lado del cliente entre estos archivos, que es lo que usualmente quieres en un ejemplo en una aplicación de NextJS, necesitas un runtime, porque entonces quieres compartir modules entre todas esas cosas, y quieres mantener entre esas cosas. Así que básicamente tienes el problema de que tienes un solo archivo de manifiesto runtime, y ahora ves el problema. Todo está referenciado, todos los fragmentos en tus miles de páginas están haciendo referencia al archivo de manifiesto, y eso te da el problema, como, si tú, en la página B, cambias tu imagen, se propaga a AsyncChunk4 en este caso, y luego se propaga al manifiesto, y eso invalidará todos tus archivos HTML. Y eso está bien, funciona, y solíamos hacerlo durante, como, años, pero creo que podemos hacerlo mejor.

Así que pasé un poco de tiempo en este cambio muy obvio que puedes hacer aquí. En lugar de hacer un manifiesto global con todas estas cosas, simplemente haces un manifiesto por página. Y eso cambia la propiedad de que ahora tus páginas están aisladas entre sí, y no tienes dependencia de hash entre ellas. Ahora puedes cambiar una página, o cambiar un módulo en una página, y solo invalida uno de los archivos HTML. Ese es un cambio simple, pero tiene un gran impacto. Todavía tienes la propiedad de que, como, la carga de la página inicial es rápida y está en caché y con hash de contenido. Pero ahora tienes páginas independientes. Las páginas pueden ser independientes, no hay dependencias de hash entre ellas. Eso tiene beneficios para la caché a largo plazo, pero también tiene beneficios para la caché de compilación donde puedes simplemente cachear archivos HTML entre despliegues si no cambian. Pero hay un compromiso. Ahora tienes que hacer algo para la navegación del lado del cliente.

4. Navegación del lado del cliente y Caché a largo plazo

Short description:

Cuando se realiza la navegación del lado del cliente, es crucial obtener el manifiesto y los fragmentos iniciales de la nueva página. Las solicitudes en caché inmutable no funcionarán, por lo que son necesarias las solicitudes sin caché o con caché etag. Combinar las solicitudes de datos con las solicitudes de nuevos fragmentos puede resolver este problema, como lo demuestran los Componentes del Servidor Rack.

Si realizas una navegación del lado del cliente de una página a otra, necesitas de alguna manera obtener el manifiesto y los fragmentos iniciales de la otra página. Y no puedes hacerlo con una solicitud en caché inmutable, porque entonces necesitarías incrustar el hash de contenido en la página HTML de nuevo, y eso rompería todo el sistema. Por lo tanto, necesitas hacerlo sin caché o con caché etag. Pero es un buen compromiso a hacer, porque cuando navegas a otra página, a menudo tienes que hacer una solicitud de data de todos modos. Y usualmente es bastante fácil agrupar o combinar esta solicitud de data con una solicitud para los nuevos fragmentos. Y en realidad los Componentes del Servidor Rack están haciendo eso. Simplemente agrúpalos juntos. Tenemos la única solicitud de data y fragmentos, y luego resuelves este problema de navegación del lado del cliente. Sí. Eso es para la caché a largo plazo.

5. Sacudida de Árbol y Optimización

Short description:

Otro tema que quiero cubrir es la sacudida de árbol. El desafío con la sacudida de árbol es que tienes mucho código no utilizado en tu aplicación, como bibliotecas de utilidades, bibliotecas de componentes y bibliotecas de iconos. Estas bibliotecas a menudo reexportan todos los iconos, lo que puede impactar negativamente en el tamaño del paquete. Sin embargo, al optimizar para el desarrollo, necesitamos tener cuidado con la sustitución de módulos y las dependencias de archivos. En Veprack, tenemos tres niveles de sacudida de árbol, incluyendo la sacudida de árbol del gráfico de módulos y la eliminación de módulos de efectos secundarios.

Otro tema que quiero cubrir, con suerte en el tiempo restante, es la sacudida de árbol. El desafío con la sacudida de árbol es que tienes mucho código no utilizado en tu aplicación. Tienes bibliotecas de utilidades como loaders, que agrupan muchas funciones, de las cuales quizás estás utilizando solo cuatro de ellas. O tienes bibliotecas de componentes, quizás de proyectos open-source y esas cosas. O tienes bibliotecas de iconos, que son en realidad las peores. Hay paquetes de miles de iconos, de los cuales solo usas dos. Sí. Usualmente ese es el problema.

Lo que las bibliotecas a menudo hacen es verificar, como, esta biblioteca de iconos, simplemente reexporta todos los iconos en la biblioteca, puedes importarlos fácilmente. Es una pesadilla para el tamaño del paquete. Y básicamente estás referenciando todos estos iconos en esta biblioteca. Y por lo tanto queremos hacer alguna optimization allí. Pero tenemos que tener cuidado. Porque en desarrollo, quieres usar algo como HMR, como la sustitución de módulos. Eso podría potencialmente entrar en conflicto con todas estas optimizaciones. Si omites un módulo que podría cambiar con el tiempo, podrías saber que no se usa, pero más tarde podría ser usado. Así que tienes que tener cuidado. También ten cuidado con este tipo de esquema donde cambiar un archivo afecta a muchos otros archivos. Eso no es realmente genial para HMR.

En Veprack básicamente tenemos tres niveles de sacudida de árbol. El primer nivel es la sacudida de árbol del gráfico de módulos donde solo siguiendo las importaciones en la aplicación, ya omites muchos archivos porque nunca son referenciados por otros archivos. Y ese es el nivel base donde simplemente sigue un gráfico de módulos y automáticamente sacude los archivos no utilizados. Está sucediendo básicamente automáticamente. No es realmente algo que tengamos que hacer. Es solo por design. Y el siguiente paso es la eliminación de módulos de efectos secundarios. Lo que significa que quizás un archivo Beryl en una biblioteca de iconos está marcado como efectos secundarios. Los efectos secundarios estaban en Package.json, por lo que está marcado como libre de efectos secundarios. Así que el Bundler puede omitir ese archivo si no hay exportación de ese usado. Y eso es a menudo realmente útil porque entonces puedes saltarte este archivo Beryl y todos estos iconos no utilizados y solo importar realmente ese archivo.

6. Optimizando la Sacudida de Árbol y las Unidades de Paquete

Short description:

Esto funciona omitiendo módulos en la referencia y generando código que solo hace referencia a las exportaciones utilizadas, eliminando las no utilizadas. Webpack analiza las exportaciones utilizadas, omite los módulos de efectos secundarios y genera código que omite el código no utilizado. En desarrollo, solo se utiliza la eliminación de módulos libres de efectos secundarios. El problema principal es que los módulos son la unidad más pequeña en Webpack, lo que resulta en la incorporación de exportaciones no utilizadas en los paquetes. Esto es problemático para aplicaciones más grandes. Además, la mayoría de la optimización está desactivada en desarrollo, lo cual no es ideal. Se necesita una nueva estrategia para determinar la unidad más pequeña en un paquete.

Esto funciona simplemente omitiendo los modules en la referencia siguiendo las referencias de una manera más rápida. Y el último nivel y el nivel más complejo es la sacudida de árbol de las exportaciones de módulos. Simplemente detectan en tu aplicación cuáles exportaciones de un módulo se utilizan y luego generan código para solo generar un nuevo código que solo hace referencia a estos modules, estas exportaciones que realmente se utilizan. Básicamente eliminando las exportaciones que no se utilizan.

Y en Webpack funciona teniendo muchos pasos. Así que esto es solo la parte de los dos últimos niveles de eso. Así que en Webpack en producción, básicamente primero analiza todas las exportaciones que se proporcionan, que se utilizan, y luego analiza todo el gráfico en modules y omite los modules de efectos secundarios. Y después de todo este proceso de análisis, básicamente puede generar, eliminar, como generar código que omite el código no utilizado y mutila los utilizados. Y al final, el minimizador entra en acción y la eliminación de código muerto en el minimizador elimina el código que realmente no se utiliza porque ya no se hace referencia a él.

En desarrollo, se ve mucho más ligero. Básicamente solo usamos la eliminación de modules libres de efectos secundarios porque todos los otros pasos serían demasiado costosos y especialmente romperían HMR de formas que son realmente impredecibles. Y eso no es genial. Pero sí, así que los problemas con eso son unos pocos. Así que el más grande, creo, es porque tenemos este gráfico de módulos y las exportaciones siendo un proceso separado, es básicamente también como la unidad más pequeña en Webpack. Lo que coloca en chunks, coloca en paquetes, es un módulo. Un módulo podría reducirse por exportaciones, pero aún así coloca un módulo completo en chunks. Y eso no es ideal. De hecho, puede que de la página A, estemos utilizando la exportación uno de una biblioteca. Y de la página B, estamos utilizando la exportación dos de una biblioteca. Pero al final, después del análisis, tenemos la biblioteca y detectamos que la exportación uno y dos se utiliza. Y en ambas páginas, realmente incorporamos la biblioteca con dos exportaciones y eso no es óptimo. Realmente queremos algo donde podamos colocar exportaciones en paquetes y en chunks para que podamos dividirlo y tener un código más eficiente. Pero especialmente para aplicaciones más grandes, como tu página de administración utiliza la exportación, pero entonces tú eres una aplicación orientada al usuario, realmente no la utilizas. Y no quieres llevar eso a los paquetes. También es problemático que la mayoría de eso esté desactivado en desarrollo. Lo que lo hace inutilizable para el desarrollo, pero podría ser útil para construcciones más rápidas porque puedes omitir cosas en desarrollo también. Y creo que el problema fundamental en este caso es en Webpack, funciona primero crea este gráfico de módulos, el gráfico de módulos no optimizado, y luego la optimization entra en acción y elimina cosas del gráfico de módulos de nuevo. Creo que deberíamos hacerlo mejor en eso. Y la nueva estrategia es reconsiderar cuál es la unidad más pequeña en un paquete. No deberíamos hacer un gráfico de módulos por modules.

7. Optimizando Fragmentos de Módulos y Colocación de Código

Short description:

En lugar de agrupar módulos como un todo, podemos dividirlos en fragmentos más pequeños basados en sus exportaciones. Siguiendo solo las exportaciones o fragmentos utilizados, podemos crear un gráfico optimizado más pequeño, omitiendo automáticamente el código no utilizado. Este enfoque elimina la necesidad de un gráfico no optimizado y permite una fusión eficiente de módulos utilizando el izado de alcance. Al aislar los cambios de los módulos, esta optimización proporciona beneficios para el desarrollo. Dividir los módulos en unidades más pequeñas también permite la colocación de código donde realmente se utiliza, habilitando nuevas posibilidades de optimización.

En cambio, deberíamos hacerlo por fragmentos de módulos y dividiendo los modules en piezas más pequeñas por exportaciones. Como mi biblioteca con 10 exportaciones podría dividirse en 10 fragmentos donde cada exportación es su propio fragmento. Así que hacemos la cosa del gráfico de módulos, pero a nivel de fragmento de módulo. Así que en cambio tenemos un gráfico de fragmentos de módulos donde solo seguimos las exportaciones que realmente se utilizan y no los modules, sino las exportaciones que se utilizan o los fragmentos que se utilizan. Y básicamente lo hacemos mucho, mucho más pequeño. Y luego podemos usar este comportamiento automático de un gráfico de módulos, omitiendo automáticamente todo lo no utilizado. Puede hacer eso también a nivel de exportación. Así que lo hace mucho más fácil y es mucho más eficiente.

Lo bueno es que este es un enfoque de un solo paso. Así que vamos al primer lugar. No creamos un gráfico no optimizado y lo optimizamos. Simplemente creamos un gráfico optimizado en primer lugar, que es mucho más eficiente. Pero para hacer eso, básicamente tenemos que fusionarlo de nuevo. Pero existe una optimization existente para el izado de alcance, que puede fusionar modules juntos en unidades más grandes que básicamente eliminan el costo de runtime de tener unidades más pequeñas en Bundler en primer lugar. Y para el desarrollo es realmente genial porque ahora todos tenemos el efecto de que aislamos cambios de modules.

Sí, echemos un vistazo a cómo funciona esto realmente desde el punto de vista del código. Como tengo este pequeño módulo de contador, que es básicamente un módulo simple. Es una variable de conteo. Es una exportación de incremento y obtención de conteo. Y ahora podemos dividirlo en unidades más pequeñas. Lo complicado es que los modules podrían compartir estado. En este caso, comparte esta variable de conteo compartida. Y para esto, tenemos que crear un interfragmento donde solo exportamos el estado de conteo, lo ponemos en un fragmento de módulo separado, y luego las diferentes exportaciones como incremento y obtener conteo pueden usar ese fragmento compartido. Sí, el beneficio de eso es que ahora ya no tenemos código no utilizado. Así que podemos simplemente no tenemos que agrupar eso. Y no tenemos que procesarlo en tiempo de compilación porque podemos simplemente saltarlo. Y también podríamos ahora, que es nuevo, podemos colocar el código donde realmente se utiliza. Así que podemos colocar la exportación A en este chunk. Podemos exportar B en otro chunk y tal vez en páginas separadas, tal vez en una carga asíncrona, diferentes exportaciones de una biblioteca. Eso da nuevas posibilidades para optimizar cosas.

QnA

Resumen y Preguntas y Respuestas

Short description:

Discutimos sobre el almacenamiento en caché a largo plazo, el aislamiento de páginas y el tree shaking. La nueva arquitectura permite dividir los módulos en exportaciones y colocarlos en diferentes contextos. Desarrollar herramientas que afectan a un amplio rango de desarrolladores es genial y divertido. Es como la magia del bundler que generalmente no afecta a los desarrolladores, pero a veces sí. Pasemos ahora a las preguntas técnicas de la audiencia.

Y también hace posible compilaciones más rápidas simplemente porque no creamos un gráfico no optimizado primero, creamos un gráfico optimizado en primer lugar. Y eso facilita saltar todos los pasos para una biblioteca IK. Y si no lo has verificado, podemos saltar la resolución de todas las exportaciones no utilizadas en absoluto. Y al saltar la resolución, podemos saltar la lectura, el análisis, el chunking, la code generation y el bundling y todo esto es más fácil de saltar al menos si eso no tiene efectos secundarios.

En resumen, hablamos sobre el almacenamiento en caché a largo plazo, que tiene que importar estas propiedades como nosotros queremos que los pequeños cambios de entrada resulten en pequeños cambios de salida. Y queremos que las páginas estén aisladas entre sí para que podamos desplegar cosas de manera independiente y no tener como invalidación de caché para cosas que no quieres que sean inválidas, no cambiaron. Y también miramos el tree shaking. En lugar de hacer un gráfico de módulos, hacemos un gráfico de fragmentos de módulos, lo que hace que la unidad sea más pequeña. Y ahora, debido a este cambio de architecture, ahora podemos dividir los modules en exportaciones y colocarlos en diferentes contextos, como diferentes páginas o diferentes cargas asíncronas individuales de exportaciones.

Así que eso es el final de la charla. Casi a tiempo. Genial. Gracias. Realmente disfruto tus charlas, especialmente porque se adentran tanto. Como tienes un conocimiento íntimo de TurboPack y Webpack y todas estas herramientas en las que a menudo dependo sin necesariamente mirar bajo el capó. Pero, ¿cómo es desarrollar herramientas que afectan a una gran cantidad de desarrolladores, a un amplio rango de desarrolladores también? Sí, es realmente genial. Y tal vez podrías decir que esto resulta en una gran responsabilidad o algo así. Pero no lo siento así, es más como que me divierto haciendo estas cosas geniales, haciendo estos algoritmos optimizados elegantes. Y especialmente me gusta. Todo esto de lo que hablé. Es básicamente transparente para el desarrollador. No tienen que preocuparse por ello. Es solo magia de bundler y lo que la gente a menudo describe como magia de bundler. Generalmente no te afecta en absoluto, pero a veces sí. Por lo tanto, es realmente útil tener este conocimiento. Y también me gusta hacer charlas profundas y temas profundos, incluso si el tiempo es realmente difícil de cubrir aquí. Pero es divertido. Sí, no, eres una de las personas, un héroe silencioso, no silencioso porque claramente estás educándonos a todos, pero uno de esos héroes que definitivamente hace que mi día siga funcionando bien. Entonces, ¿por qué no pasamos a algunas de las preguntas técnicas que vinieron de la audiencia? No olvides que puedes hacer tus preguntas.

Uso de TurboPack y relación con Webpack

Short description:

Tenemos planes de hacer que TurboPack sea utilizable fuera de Next.js, pero actualmente no es lo suficientemente estable para su uso independiente. Existe un binario CLI de TurboPack que puede ejecutar una aplicación de una sola página, pero aún no se recomienda su uso público. TurboPack no es solo una nueva versión de Webpack, sino más bien un sucesor con una arquitectura, lenguaje, configuración y API de complementos diferentes.

También tenemos la pequeña pantalla en el frente también. Entonces estás muy, muy cerca de TurboPack y viendo la hoja de ruta y probablemente hay cosas aún más lejanas de las que puedes hablar y que tienes una idea.

Entonces, ¿cuál es el cronograma, si lo hay, para usar TurboPack fuera de Next.js? Sí. Aún no hemos hecho un plan para eso, pero está planeado hacerlo eventualmente. Queremos terminar con Next.js. Así que realmente resolvimos todos los problemas para eso y no tenemos que hacer grandes refactorizaciones de nuevo una vez que sea público, especialmente si probablemente lo hagamos independiente, sea utilizable de forma independiente, hace que la API sea al menos un poco pública. Queremos hacerlo lo más estable posible y resolver eso para Next.js y luego podemos hacerlo independiente.

Pero técnicamente hay un binario que puedes compilar tú mismo, que es TurboPack CLI, que puede ejecutar una aplicación de una sola página. No tiene ninguna configuración ni complementos, pero técnicamente es posible, creo que podría hacer una aplicación directa, algo similar. Entonces estás diciendo que lo hagas bajo tu propio riesgo. Sí. No deberías hacer eso. Aún no es público, pero lo hicimos para nuestros propios propósitos, nuestros propios casos de prueba, uno con nuestro básicamente en un TurboPack independiente. Sí. Pero bien, bien, bien. Y otra cosa también, porque creo que esto es lo que hablamos, porque cuando TurboPack fue presentado como nuevo, parece que es una evolución de Webpack. ¿Por qué no solo aumentar la versión y qué llevó a la creación de una herramienta totalmente nueva? Porque, especialmente con TurboPack, estás construyendo una herramienta dentro del contexto de Next.js. Está muy estrechamente vinculado al hecho de que se va a utilizar mucho en este ecosistema, mientras que con Webpack, tienes una audiencia mucho más amplia a la que atiendes.

Sí. Estamos apuntando a esta audiencia más amplia eventualmente. Básicamente me habría sentido mal si es como un Webpack 6 o 7 o 10 o lo que sea, porque sería un cambio tan importante, especialmente porque no admitimos los complementos de Webpack de la misma manera. Y no admitimos la configuración de Ops de la misma manera. Y no siento que esto sea como una nueva versión de Webpack, porque eso sería como un cambio y cambio. Cambia todo. Es como una nueva architecture, un lenguaje completamente diferente. Así que no creo que puedas afirmar que es como una nueva versión de Webpack. Es más como algo que se inspiró en Webpack o tiene el mismo propósito que Webpack, pero no es la próxima versión de él. Es más como, lo llamamos un sucesor, pero es como, sí, es una nueva herramienta que hace lo mismo en términos de casos de uso, pero no es la misma herramienta.

Personalizando la Configuración y Riesgos de Hash

Short description:

Tomamos algunas cosas de Webpack y rediseñamos la interfaz de configuración y complementos para abordar las luchas comunes. Aún no hemos creado una configuración para evitar una complejidad abrumadora. Al desarrollar y probar internamente, podemos iterar y mejorar la API antes de hacerla pública. En cuanto a los hashes para el almacenamiento en caché, existe un riesgo de conflictos de falsos positivos, pero la longitud del hash suele ser suficiente para prevenir ocurrencias prácticas. Es más probable ganar la lotería que encontrar conflictos de hash.

Es como una configuración diferente, una API de complemento diferente. Sí. Todo es diferente. Así que intentamos, sí, al menos tomamos algunas cosas de Webpack. El runtime se parece mucho a Webpack. La forma en que se generan los modules en los chunks es bastante similar. Así que usamos una buena parte de Webpack e intentamos rehacer las malas partes como la configuración, la interfaz del complemento, con lo que la gente realmente lucha.

Y sí, aún no hemos hecho ninguna configuración porque básicamente, tenemos miedo de hacer, si creamos una configuración, como crear una API, que se quedará por un tiempo y eso es difícil de cambiar después. Sí. No queremos cometer el mismo error con Webpack donde una configuración es realmente abrumadora y complicada de usar. Y eso aunque como una ventaja de hacerlo dentro de Next.js, es que no tenemos que exponer ninguna configuración, ninguna API de Webpack aún. Podemos hacer que funcione en Next.js y como iterar en la API, en las cosas internas tanto como queramos antes de hacerlo.

Es como si estuvieras perfeccionando la receta en casa antes de abrir el restaurante. Es como eso. Así que en realidad, como lo hacemos mucho, primero probamos nuestras propias herramientas internamente y luego antes de hacerlo público. Y porque en realidad ayuda a que nuestra propia gente trabaje en eso y trabaje con eso y lo pruebe y tal vez encuentre los problemas. Y luego podemos iterar en eso antes de hacerlo público. Y luego, con suerte, es un buen producto al final.

Eso es genial. También estoy esperando eso. Otra persona preguntó, porque hablaste de los hashes cuando estábamos hablando de almacenamiento en caché, ¿cómo se calcula el hash? ¿Y hay algún riesgo o falsos positivos? Existe un riesgo de conflictos de hash de falsos positivos, más bien. Pero generalmente los hashes son lo suficientemente largos como para que eso no suceda en la práctica. Pero sí, siempre es un riesgo si usas hashing, puedes tener conflictos y eso tendría la misma URL y la causa de esas cachés. Pero en la práctica esto nunca sucede porque creo que es un hash de ocho bytes o algo así. Es tan grande y el conflicto es tan improbable que no sucede. En ese punto, también deberías ir y ganar la lotería. Sí, es probablemente más probable ganar la lotería que los conflictos de hash. Pero también estás emitiendo muchos archivos. Así que técnicamente es posible, pero nunca veo ningún caso en eso. Vi algunos casos en los que la gente, puedes configurar la longitud del hash y la gente tiende, algunas personas intentan hacerlo lo más corto posible, como cuatro caracteres, y eso en realidad entra en conflicto a menudo.

Importaciones de Barril y División de Código

Short description:

Y luego la gente se encuentra con problemas con eso. Pero si mantienes la longitud normal de ocho caracteres, que es bastante alta, debería estar bien en la mayoría de los casos. Recientemente enviamos un paquete de herramientas de optimización que maneja eficientemente las importaciones de barril, permitiéndote seguir utilizándolas sin afectar tu construcción. Para la división de código, es importante considerar qué módulos van en qué fragmentos. El enfoque de Webpack de dividir módulos comunes puede ser ineficiente y problemático para el almacenamiento en caché a largo plazo. En el desarrollo, nos enfocamos en hacer cambios aislados a las páginas y asegurarnos de que cada punto de entrada solo se preocupe por sí mismo.

Y luego la gente se encuentra con problemas con eso. Pero si mantienes la longitud normal de ocho caracteres, que es bastante alta, eso debería estar bien en la mayoría de los casos.

Genial. Genial. Es increíble. Las preguntas siguen llegando una tras otra. No podremos responder a todas, pero vamos a responder a tantas como podamos. Y esta es sobre las importaciones de barril. Entonces, ¿existe una buena alternativa a las importaciones de barril donde las importaciones siguen siendo legibles, pero no afectas tu construcción y obtienes dependencias circulares?

Sí. De hecho, recientemente enviamos un paquete de herramientas de optimization, que hace algo similar al árbol compartiendo lo que tenemos en el nivel superior, pero solo para las exportaciones. Y eso debería hacerlo, si escribes un archivo de barril con exportaciones nombradas, no, no con export star from export star, export blah from something, entonces será, como, básicamente no tiene costo de este archivo de barril. Simplemente omite las exportaciones. Tienes que marcarlo como efecto secundario tres, por supuesto. Pero puede omitir eso. Y para el total pack, no habría ningún costo para este archivo de barril. Así que en realidad puedes seguir utilizando archivos de barril. Y no recomendaría hacer eso ahora si fuera una biblioteca porque otras herramientas y como webpack no manejan muy bien los archivos de barril. Básicamente pasa y luego hace todas las otras importaciones. Pero al menos para total pack hicimos el cambio de que podemos hacerlo eficiente para escribir archivos de barril para hacer que el código parezca amigable, pero aún teniendo una buena optimization.

Eso es increíble. Y la siguiente es más sobre la implementación. Entonces, ¿cómo implementamos la división de código y cuáles son las consideraciones más importantes cuando estamos haciendo eso?

Sí. La división de código es realmente interesante, especialmente cómo decides qué módulo va en qué fragmentos. Entonces, webpack hizo, como, esta cosa de gráfico completo que, como, ventana dividida y común modules están todos divididos. Y eso es realmente ineficiente para el gusto de todos. Pero también es, como, problemático para el almacenamiento en caché a largo plazo. Si extraes código común, entonces a menudo tienes este problema de que hay esta referencia de múltiples fragmentos. Así que actualmente en desarrollo no hacemos este código común. Básicamente intentamos hacer cambios aislados a las páginas. Entonces, como, un punto de entrada solo debería preocuparse por este punto de entrada.

Puntos de Entrada y Heurísticas de Fragmentación

Short description:

No mira otros puntos de entrada. Tiene mucho sentido desde la experiencia de rendimiento del desarrollador. En producción, queremos hacer algo similar a Webpack, extrayendo código común y utilizando heurísticas. Una heurística es poner los módulos de nodo y el código de la aplicación en diferentes fragmentos para el almacenamiento en caché a largo plazo. Hay tanto detalle que se puede explorar, pero las limitaciones de tiempo nos impiden profundizar en ello. Si tienes más preguntas, por favor únete a Tobias en la discusión de preguntas y respuestas.

No mira otros puntos de entrada. Tiene mucho sentido desde la experiencia de rendimiento del desarrollador. Porque entonces no tienes que preocuparte por estas páginas. Y cambiar una página o añadir una página no afecta a otras páginas. Eso es realmente importante para nosotros en desarrollo.

Y en producción, probablemente aún no lo hemos hecho. Pero probablemente queremos hacer algo similar a webpack. Tenemos una información de aplicación global y, como, extraemos cosas de código común. Sí. Pero también necesitamos hacer algunas cosas heurísticas. Como, una heurística en webpack es, como, los modules de nodo van a un fragmento diferente. Y el código de la aplicación va en otro fragmento. Así que eso lo hace posible. Porque tiene sentido desde una perspectiva de almacenamiento en caché a largo plazo. Porque el código del proveedor cambia menos a menudo que el código de la aplicación. Y entonces si lo aíslas en diferentes archivos, puedes aprovechar el almacenamiento en caché a largo plazo para los proveedores mucho más tiempo que si lo juntaras. Y cambiar el código de la aplicación afectaría al código del proveedor. Y tendrías que volver a descargar eso. Bien. Muchas consideraciones. Pero es en su mayoría heurísticas y otras cosas. Hay, como, tanto detalle en el que puede entrar.

Sí, definitivamente. Y es realmente complicado. Sí. Bueno, no tenemos suficiente tiempo para entrar en todo eso. Pero había muchas más preguntas. Y si tienes más preguntas, por favor ven y consulta a Tobias en la discusión de preguntas y respuestas Q&A. Y estarás presente durante la conferencia, y la gente puede encontrarte en línea. Demosle un aplauso, damas y caballeros. Gracias. Y... Gracias. Gracias. Gracias. Gracias por venir. Gracias.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Building Figma’s Widget Code Generator
React Advanced Conference 2022React Advanced Conference 2022
19 min
Building Figma’s Widget Code Generator
Widgets are custom, interactive objects you place in a Figma or Figjam file to extend functionality and make everything a bit more fun. They are written in a declarative style similar to React components, which gets translated to become a node on the canvas. So can you go the other way, from canvas to code? Yes! We’ll discuss how we used the public Figma plugin API to generate widget code from a design file, and make a working widget together using this.
Start Building Your Own JavaScript Tools
JSNation 2023JSNation 2023
22 min
Start Building Your Own JavaScript Tools
Your first JavaScript tool might not be the next Babel or ESLint, but it can be built on them! Let's demystify the secret art of JavaScript tools, how they work, and how to build our own. We'll discover the opportunities in our everyday work to apply these techniques, writing our own ESLint rules to prevent mistakes and code transforms to make breaking changes easy to apply. We’ll walk through the fundamentals of working with an abstract syntax tree, and develop our understanding through a live-code. You will be amazed at what you can build, and together we’ll explore how to get started.
Advanced linting rules with ESLint
TypeScript Congress 2023TypeScript Congress 2023
10 min
Advanced linting rules with ESLint
This talk will explore more advanced ways to write static analysis rules in ESLint using ESLint's control flow APIs. I will quickly explain what a control flow graph is and how you can use it to find issues in your code. I will show you how to detect when a value is assigned to variable uselessly and other logical problems you can detect using this technique.
How not(!) to Build Real-time Apps
Node Congress 2024Node Congress 2024
10 min
How not(!) to Build Real-time Apps
Are you building a chat app, a way to see users’ online status or a real-time collaboration dashboard? All of these use cases have one thing in common: Somehow the user-facing application needs to be informed in real-time about events that happen on the backend of your application.In this talk, we’ll look closely at common approaches like polling, application-level updates and pub-sub systems. We’ll explain the tradeoffs with each approach and elaborate why another approach, called Change Data Capture (CDC), is the most elegant and robust way to achieve this.
Building a Network Stack for our Browser Extension
Node Congress 2024Node Congress 2024
19 min
Building a Network Stack for our Browser Extension
Engineering problems often repeat themselves in places you wouldn't expect. Sometimes the best solution has already been invented, in a different corner of the software engineering domain. In this talk, we show how and why we mirrored the TCP/IP network stack to solve a communication problem between different components of a browser extension.

Workshops on related topic

Build React-like apps for internal tooling 10x faster with Retool
JSNation Live 2021JSNation Live 2021
86 min
Build React-like apps for internal tooling 10x faster with Retool
Workshop
Chris Smith
Chris Smith
Most businesses have to build custom software and bespoke interfaces to their data in order to power internal processes like user trial extensions, refunds, inventory management, user administration, etc. These applications have unique requirements and often, solving the problem quickly is more important than appearance. Retool makes it easy for js developers to rapidly build React-like apps for internal tools using prebuilt API and database interfaces as well as reusable UI components. In this workshop, we’ll walk through how some of the fastest growing businesses are doing internal tooling and build out some simple apps to explain how Retool works off of your existing JavaScript and ReactJS knowledge to enable rapid tool building.
Prerequisites:A free Retool.com trial accountSome minimal JavaScript and SQL/NoSQL database experience
Retool useful link: https://docs.retool.com/docs