DEV Community

Cover image for Importando módulos en Javascript, lo estamos haciendo bien?
Diego Juliao for ngVenezuela

Posted on

Importando módulos en Javascript, lo estamos haciendo bien?

Todos los desarrolladores Javascript usamos librerías en nuestro día a día, facilita mucho nuestro trabajo. Hoy en día lo hacemos mayormente de esta manera:

import * as lib from 'super-lib'
import { func1 } from 'super-lib'
Enter fullscreen mode Exit fullscreen mode

Si analizamos rápidamente esto, en la primera sentencia estamos importando todo y asignándolo a la variable lib, en el segundo, estamos importando todo nuevamente pero usando destructuring obtenemos solo lo que queremos; qué sucede con todo el código que no se está usando?

¿Todo el código que no se está usando terminará en el bundle final haciendo mi aplicación innecesariamente pesada?

Hoy, aprenderemos cómo mejorar el tamaño de un bundle simplemente cambiando la manera en que importamos. Después de leer esto, serás capaz de detectar una simple oportunidad de optimización para el bundle.


TL; DR

Verifica si la libería tiene soporte para ES6 y serás capaz de importar como quieras, siempre obtendrás el mejor resultado 🙆‍♂️. Si no lo soporta ⚠️, tendrás que importar usando cherry-picking.


¿Podemos importar de cualquier manera sin consecuencias?

Cuando compilamos aplicaciones front-end, hay un proceso que Webpack aplica llamado Tree Shaking. Básicamente, es eliminación de código, el código que no está siendo usado por nadie. Esto es un proceso que previene que código muerto termine en nuestro bundle final, haciéndolo más liviano así las aplicaciones van a cargar más rápidamente para nuestros usuarios!.

Analicemos esto:

import * as lib from 'amazing-lib'
import { foo } from 'amazing-lib'
Enter fullscreen mode Exit fullscreen mode

En ambos casos todo el contenido de la librería está siendo importado, en la primera línea es donde se observa más claramente, todo el contenido está siendo asignado a la variable lib, en la segunda línea estamos simplemente aplicando destructuring al contenido de la librería para obtener lo que queremos. Gracias a Tree Shaking todo el código que no es usado no termina en el bundle final.

Así que, gracias a Tree Shaking tengo un pase libre a importar como yo quiera y todo el código muerto importado de la librería será removido automágicamente?

No siempre es el caso

Hay un escenario cuando Tree Shaking no será capaz de detectar qué es código muerto, teniendo como consecuencia que no se removerá nada.

Escenarios

ES6

La Sintaxis de Modulo ECMAScript 2015(también conocido como ES6); suena complejo, pero es algo bastante popular en estos días. Es solo una sintaxis para importar un modulo Javascript, luce así:

import { foo } from 'super-lib'
import { bar } from '../utils'
import * as lib from '../utils'

export const justAConst = 'foobar'
Enter fullscreen mode Exit fullscreen mode

Cuando usas una librería que ofrece soporte para para sintaxis ES6, no hay de qué preocuparse, importa libremente, Tree Shaking estará ahí 😉. De hecho, es con la única sintaxis de modulo que Tree Shaking soporta. Miremos a la documentación:

Tree Shaking es un término comúnmente usado en el contexto Javascript para la eliminación del código muerto. Él se apoya en la estructura estática de la sintaxis de modulo ES2015, esto es import y export...

El lanzamiento de Webpack 2 vino con soporte para los módulos ES2015 (también conocido como módulos de armonía) junto a la detección de módulos exportados que no están siendo usados...

Si eres completamente nuevo acerca de los 👉 Módulos JS

Sin sintaxis de modulo ES6

Una librería puede ser empaquetada con otro sistema de módulos diferente a ES6, un proceso de compilación pudo haberse implementado para soportar solo CommonJS por ejemplo. Los proyectos escritos usando Javascript puro (sin procesos de transpiración (Babel, TypeScript)) que usan unicamente CommonJs para gestionar sus módulos es otro ejemplo.

Así que, sin sintaxis ES6 significa no Tree Shaking. La única manera de tener un bundle saludable cuando se lidia con librerías sin soporte para la sintaxis de módulo ES6 es importar usando una técnica llamada cherry-picking, se necesita especificar la ruta absoluta al archivo que contiene la información que necesitamos.

import { small } from 'common-js-lib/small';
Enter fullscreen mode Exit fullscreen mode

Inconvenientes del cherry-picking

  • Es necesario saber la ruta al modulo necesitado. (Tu IDE puede ayudar en esto)
  • Es necesario especificar uno a uno los módulos requeridos, ej:

    import has from 'lodash/has';
    import capitalize from 'lodash/capitalize';
    import lastIndexOf from 'lodash/lastIndexOf';
    
  • Como autor de una librería, probablemente querrás un sistema de carpetas fácil de usar para detectar rápidamente algo en tu librería.

  • Puedes olvidar hacerlo y sin querer se hace el bundle innecesariamente más pesado. EsLint puede ayudarte a importar correctamente.

Test de Performance

Teniendo la teoría aprendida decidí probar todo esto. Lo que hice fue crear algunas librerías con soporte para diferentes módulos, crear algunas aplicaciones front-end con Angular y React1 para probar si Tree Shaking estaba realmente haciendo su trabajo.

Las librerías creadas fueron simples, ellas exportan dos variables small y big. small contiene un perro 🐕 (small = '🐕'), pero big tiene 1646400 perros (big = '🐕🐕🐕🐕🐕🐕🐕🐕🐕...'). Esto hará que big pese 6.3 megabytes.

Únicamente será usado small todo el tiempo, así que si big logra escabullirse hasta el bundle se podrá notar de inmediato.

Bundle Saludable

Así es como un bundle saludable se ve:
bundle saludable, de 211.78KB

Bundle Oloroso 🤢

El bundle oloroso! Puedes notar una gran caja blanca que representa el 96.7% de la aplicación: Bundle Oloroso, bundle de 6.49MB

Los Resultados

Los resultados fueron los esperados, si la libería tenía para ofrecer la sintaxis de módulos de ES6, Tree Shaking hará su trabajo. Si no, cherry-picking es el único camino hacia el bundle saludable.

Aquí está el repositorio si son de los curiosos dianjuar/how-to-import. Todo esto fue creado en un monorepo usando Nx, para simular la publicación del paquete en NPM se usó yalc. El análisis del bundle fue hecho con source-map-explorer.

Yo también quise aplicar este test a algunas librerías conocidas y esto fue lo que encontré al importar de la siguiente manera import { whatINeed } from 'popular-lib'

Librería Bundle Saludable
lodash
moment
rxjs
lodash-es
date-fns
@angular/core
@angular/material
react
react-dom
@material-ui/core
@fortawesome/react-fontawesome

Herramientas Útiles

Durante este experimento estuve usando la extensión de VsCode Import Cost y fue precisa paralelamente con los resultados. Con esta extensión podrán ver de manera inmediata cuánto costará el import en el bundle. No dirá directamente si estás teniendo un bundle saludable o no, pero te darás cuenta cuándo un import es sospechoso.

Import Cost Live

Gif extraído del README de Cost Import

Conclusión

Tree Shaking te tiene cubierto! Puedes importar como desees y siempre tendrás un bundle saludable si y solo si la librería tiene soporte para la sintaxis de módulos de ES6 (import y export).

Puedes hacer el bundle innecesariamente pesado si no haces cherry-pick al importar en librerías sin soporte de módulos ES6, como lodash.

Import Cost te puede ayudar a detectar un import que necesite algún refinamiento.



  1. El experimento es agnóstico al framework o librería usada, si webpack está encargado de generar el bundle el resultado sería el mismo. 

Top comments (0)