En abril de este año, publicamos una traducción del
primer material de una serie dedicada a un enfoque responsable para el desarrollo de JavaScript. Allí, el autor reflexionó sobre las tecnologías web modernas y su uso racional. Ahora le ofrecemos una traducción del segundo artículo de esta serie. Está dedicado a algunos detalles técnicos sobre el diseño de proyectos web.

Tener una idea
Usted y su equipo promovieron con entusiasmo la idea de una revisión completa del antiguo sitio web de la compañía. Sus solicitudes llegaron al liderazgo, incluso llegaron a la vista de aquellos en la cima. Te dieron luz verde. Su equipo se puso a trabajar con entusiasmo, atrayendo diseñadores, redactores y otros especialistas. Pronto lanzaste un nuevo código.
El trabajo comenzó inocentemente. El
npm install
está aquí, el
npm install
está allí. Tan pronto como miró a su alrededor, las dependencias de producción ya se estaban estableciendo como si el desarrollo del proyecto fuera una bebida salvaje, y usted fuera el que no se preocupaba en absoluto por lo que sería mañana por la mañana.
Entonces empezaste.
Pero, a diferencia de las consecuencias de la fiesta de bebidas más loca, lo terrible no comenzó a la mañana siguiente. Lamentablemente, no a la mañana siguiente. El cálculo llegó en meses. Tomó la forma desagradable de náuseas leves y dolor de cabeza para los dueños de empresas y gerentes intermedios que se preguntaban por qué, después del lanzamiento del nuevo sitio, las conversiones y los ingresos cayeron. Entonces el desastre ganó impulso. Esto sucedió cuando el director técnico regresó del fin de semana, que pasó en algún lugar fuera de la ciudad. Se preguntó por qué el sitio web de la compañía se cargaba tan lentamente (si es que lo hizo) en su teléfono.
Solía ser bueno para todos. Ahora han llegado otros tiempos oscuros. Conoce tu primera resaca después de consumir una gran dosis de JavaScript.
Esto no es tu culpa.
Mientras intentabas hacer frente a una resaca infernal, palabras como "te lo dije" sonarían como una merecida reprimenda para ti. Y si pudieras pelear en ese momento, podrían servir como una ocasión para pelear.
Cuando se trata de las consecuencias del uso imprudente de JavaScript, puede culpar a todo y a todos. Pero buscar al culpable es una pérdida de tiempo. El dispositivo web moderno requiere que las compañías resuelvan los problemas más rápido que sus competidores. Tal presión significa que nosotros, esforzándonos por aumentar nuestra productividad tanto como sea posible, es probable que agarremos cualquier cosa. Esto significa que nosotros, con un alto grado de probabilidad (aunque esto no se puede llamar inevitable), crearemos aplicaciones en las que habrá muchos excesos y, muy probablemente, usaremos patrones que perjudiquen el rendimiento y la disponibilidad de las aplicaciones.
El desarrollo web no es una tarea fácil. Este es un trabajo largo. Raramente se realiza bien en el primer intento. Sin embargo, lo mejor de este trabajo es que no tenemos que hacer todo perfectamente desde el principio. Podemos realizar mejoras en los proyectos después de su lanzamiento y, de hecho, este material está dedicado a esto, el segundo de una serie de artículos sobre un enfoque responsable para el desarrollo de JS. La perfección es un objetivo muy lejano. Mientras tanto, lidiemos con la resaca de JavaScript mejorando, por así decirlo, las
secuencias de comandosen el sitio en un futuro cercano.
Nos ocupamos de problemas comunes
Esto puede parecer un enfoque mecánico para resolver problemas, pero primero revise una lista de problemas típicos y formas de tratarlos. En grandes equipos de desarrollo, estas cosas a menudo se olvidan. Esto es especialmente cierto para aquellos equipos que trabajan con múltiples repositorios o no usan una plantilla optimizada para sus proyectos.
▍ Aplicar algoritmo de sacudida de árbol
Primero, verifique si sus herramientas están configuradas para implementar el algoritmo de
sacudimiento de árbol . Si no se ha encontrado con este concepto antes, eche un vistazo a este material
mío escrito el año pasado. Si explicamos el funcionamiento de este algoritmo en pocas palabras, podemos decir que debido a su uso en los ensambles de producción de la aplicación no incluye aquellos paquetes que, aunque importados al proyecto, no se usan en él.
La implementación del algoritmo de agitación de árbol es una característica estándar de los paquetes modernos como
webpack ,
Rollup o
Parcel .
Grunt o
trago son administradores de tareas. Ellos no hacen esto. El administrador de tareas, a diferencia del agrupador, no crea un
gráfico de dependencia . El administrador de tareas participa, utilizando los complementos necesarios, realizando manipulaciones separadas en los archivos que se le transfieren. La funcionalidad de los administradores de tareas se puede ampliar usando complementos, dándoles la capacidad de procesar JavaScript usando paquetes. Si ampliar las capacidades del administrador de tareas en esta dirección parece ser un problema, entonces probablemente deba verificar manualmente la base del código y eliminar el código no utilizado.
Para que el algoritmo de vibración de árbol funcione de manera eficiente, se deben cumplir las siguientes condiciones:
- El código de aplicación y los paquetes instalados deben presentarse como módulos ES6 . Usar el algoritmo de vibración de árbol para módulos CommonJS es casi imposible.
- Su paquete no debe transformar los módulos ES6 en módulos de algún otro formato durante la compilación del proyecto. Si esto sucede en la cadena de herramientas que usa Babel, entonces @ Babel / present-env debe tener los módulos: configuración falsa . Esto hará que el código ES6 no se convierta en código que use CommonJS.
Si de repente, al construir su proyecto, el algoritmo de sacudida del árbol no se aplica, la inclusión de este mecanismo puede mejorar la situación. Por supuesto, la efectividad de este algoritmo varía de un proyecto a otro. Además, la posibilidad de su uso depende de si los módulos importados tienen
efectos secundarios . Esto puede afectar la capacidad del agrupador para deshacerse de la inclusión de módulos importados innecesarios en el ensamblaje.
▍Dividir el código en partes
Es muy probable que ya esté utilizando alguna forma de
separación de
código . Sin embargo, debe verificar cómo se hace esto. Independientemente de cómo separe el código, quiero ofrecerle que se haga las siguientes dos preguntas muy valiosas:
- ¿Elimina el código duplicado de los puntos de entrada ?
- ¿Carga perezosamente todo lo que se puede cargar de esta manera con importaciones dinámicas ?
Estos problemas son importantes porque reducir la cantidad de código redundante es un elemento de rendimiento crucial. La carga lenta del código también mejora el rendimiento al reducir la cantidad de código JavaScript que forma parte de la página y se carga cuando se carga. Si hablamos sobre el análisis del proyecto para detectar la presencia de código redundante en él, entonces para esto puede usar algún tipo de herramienta como Bundle Buddy. Si su proyecto tiene un problema con esto, esta herramienta le informará al respecto.
La herramienta Bundle Buddy puede verificar la información de compilación del paquete web y descubrir cuánto se usa el mismo código en sus paquetesSi hablamos de carga diferida de materiales, descubrir dónde buscar oportunidades para aplicar esta optimización puede ser de cierta dificultad. Cuando investigo un proyecto existente para la posibilidad de usar carga diferida, busco en la base del código aquellos lugares que involucran interacciones del usuario con el código. Puede ser, por ejemplo, controladores de eventos de mouse o teclado, así como otras cosas similares. Cualquier código que requiera alguna acción del usuario para ejecutarse es un buen candidato para aplicarle el comando dinámico
import()
.
Por supuesto, cargar scripts a pedido conlleva el riesgo de retrasos notables al mover el sistema al modo interactivo. De hecho, antes de que el programa pueda interactuar con el usuario de manera interactiva, debe descargar el script apropiado. Si la cantidad de datos transferidos no le molesta, considere usar la sugerencia de recurso
rel = prefetch para cargar scripts de baja prioridad. Dichos recursos no competirán por el ancho de banda con recursos críticos. Si el navegador del usuario
admite rel=prefetch
, usar esta información sobre herramientas solo será beneficioso. De lo contrario, no pasará nada malo, ya que los navegadores simplemente ignoran el marcado que no entienden.
▍Utilice la opción externa del paquete web para marcar recursos ubicados en servidores extranjeros
Idealmente, debe alojar tantas dependencias de su sitio en sus propios servidores como sea posible. Si por alguna razón usted, sin opciones, tiene que descargar dependencias de los servidores de otras personas, colóquelas en el bloque
externo en la configuración del paquete web. Si no se hace esto, esto puede significar que los visitantes de su sitio descargarán tanto el código que aloja como el mismo código de los servidores de otras personas.
Eche un vistazo a una situación hipotética en la que algo como esto podría dañar su recurso. Suponga que su sitio descarga la biblioteca Lodash de un recurso público de CDN. También instaló Lodash en el proyecto para fines de desarrollo local. Sin embargo, si no especifica en la configuración del paquete web que Lodash es una dependencia externa, su código de producción cargará la biblioteca desde el CDN, pero al mismo tiempo se incluirá en el paquete que está alojado en su servidor.
Si conoce bien los paquetes, entonces todo esto puede parecerle verdades comunes. Pero vi cómo estas cosas no están prestando atención. Por lo tanto, no se tome el tiempo de verificar dos veces su proyecto por los problemas anteriores.
Si no considera necesario alojar sus dependencias creadas por desarrolladores de terceros usted mismo, entonces considere usar
dns-prefetch ,
preconnect o quizás incluso
precargue sugerencias con ellos. Esto puede reducir el puntaje de
TTI (Time To Interactive, tiempo del sitio hasta la primera interactividad). Y si se requieren capacidades de JavaScript para mostrar el contenido del sitio,
también se necesita el
Índice de velocidad del sitio.
Bibliotecas alternativas más pequeñas y gastos generales reducidos en los sistemas de usuario.
Lo que se llama "
Userland JavaScript " (bibliotecas JS desarrolladas por el usuario) parece una enorme tienda de dulces obscena. Toda esta magnificencia y variedad de código abierto nos inspira, desarrolladores, con asombro. Los marcos y las bibliotecas nos permiten expandir nuestras aplicaciones, equipándolas rápidamente con funciones que ayudan a resolver una variedad de problemas. Si tuviéramos que implementar la misma funcionalidad por nuestra cuenta, tomaría mucho tiempo y energía.
Aunque personalmente defiendo la minimización agresiva del uso de marcos de clientes y bibliotecas en mis proyectos, no puedo dejar de reconocer su enorme valor y utilidad. Pero, a pesar de esto, cuando se trata de instalar nuevas dependencias en el proyecto, debemos tratar a cada uno de ellos con bastante sospecha. Si ya hemos creado y lanzado algo, cuya operación depende de muchas dependencias instaladas, esto significa que soportamos la carga adicional en el sistema que todo esto crea. Es probable que solo los desarrolladores de paquetes puedan resolver este problema optimizando su desarrollo. Es asi?
Quizás esto sea así, pero quizás no. Depende de las dependencias utilizadas. Por ejemplo, React es una biblioteca extremadamente popular. Pero
Preact es una alternativa
muy pequeña a React, que le da al desarrollador casi las mismas API y sigue siendo compatible con muchos complementos de React.
Luxon y
date-fns son alternativas a
moment.js , mucho más compactas que esta biblioteca, que
no es
tan pequeña .
En bibliotecas como
Lodash, puede encontrar muchos métodos útiles. Pero algunos de ellos son fáciles de reemplazar con los métodos estándar de ES6. Por ejemplo, el método
compacto Lodash se puede reemplazar con el método estándar de matrices de
filtros .
Muchos otros métodos de Lodash también se pueden reemplazar de forma segura por otros estándar. La ventaja de este reemplazo es que obtenemos las mismas características que al usar la biblioteca, pero eliminamos una dependencia bastante grande.
Independientemente de lo que use, la idea general sigue siendo la misma: pregunte si su elección tiene alternativas más compactas. Averigüe si puede resolver los mismos problemas con las herramientas de lenguaje estándar. Quizás se sorprenderá gratamente de lo poco que tiene que hacer para reducir seriamente el tamaño de la aplicación y la cantidad de carga innecesaria que ejerce sobre los sistemas del usuario.
Usar tecnologías de carga diferencial de script
Lo más probable es que Babel esté en tu cadena de herramientas. Esta herramienta se utiliza para transformar el código fuente compatible con ES6 en código que los navegadores heredados pueden ejecutar. ¿Significa esto que estamos condenados a dar grandes paquetes incluso a los navegadores que no los necesitan, hasta que todos los navegadores antiguos simplemente desaparezcan?
Por supuesto que no ! La carga diferencial de recursos ayuda a evitar este problema al crear dos ensamblajes diferentes basados en el código ES6:
- El primer ensamblaje incluye todas las conversiones de código y polyfill necesarios para que su sitio funcione en navegadores heredados. Probablemente ahora le está dando a los clientes esta asamblea particular.
- El segundo ensamblaje contiene un mínimo de conversiones de código y polyfill, o no tiene ninguna. Está diseñado para navegadores modernos. Esta es una asamblea que quizás no tengas. Al menos todavía no.
Para utilizar la tecnología de carga diferencial de conjuntos, debe trabajar un poco. No entraré en detalles aquí. Daré un mejor
enlace a mi material, que analiza una de las formas de implementar esta tecnología. La esencia de todo esto es que puede modificar su configuración de compilación para que durante la compilación del proyecto se cree una versión adicional del paquete JS de su sitio. Este paquete adicional será más pequeño que el principal. Estará destinado solo a los navegadores modernos. La mejor parte es que este enfoque le permite optimizar el tamaño del paquete y, al mismo tiempo, no sacrificar absolutamente nada de las capacidades del proyecto. Dependiendo del código de la aplicación, ahorrar en el tamaño del paquete puede ser bastante significativo.
Análisis de paquetes para navegadores heredados (izquierda) y paquetes para nuevos navegadores (derecha). El estudio de paquete se realizó utilizando webpack-bundle-analyzer. Aquí está la versión a tamaño completo de esta imagen.Es más fácil dar diferentes paquetes a diferentes navegadores utilizando el siguiente truco. Funciona bien en los navegadores modernos:
<!-- : --> <script type="module" src="/js/app.mjs"></script> <!-- : --> <script defer nomodule src="/js/app.js"></script>
Desafortunadamente, este enfoque tiene desventajas. Los navegadores obsoletos como IE11, e incluso los relativamente modernos, como las versiones Edge 15-18, cargarán ambos paquetes. Si está listo para soportarlo, utilice esta técnica y no se preocupe por nada.
Por otro lado, debe encontrar algo en caso de que le preocupe el
impacto en el rendimiento de su aplicación del hecho de que los navegadores más antiguos tienen que descargar ambos paquetes. Aquí hay una posible solución a este problema que utiliza la inyección de script (en lugar de la etiqueta
<script>
que usamos anteriormente). Evita la carga doble de paquetes por los navegadores apropiados. Esto es de lo que estamos hablando:
var scriptEl = document.createElement("script"); if ("noModule" in scriptEl) {
Este script asume que si el navegador admite el atributo
nomodule en el elemento del
script
, entonces comprende la construcción
type="module"
. Esto garantiza que los navegadores heredados solo recibirán scripts diseñados para ellos, y los modernos recibirán scripts diseñados para ellos. Sin embargo, tenga en cuenta que las secuencias de comandos incrustadas dinámicamente se cargan de forma asincrónica de forma predeterminada. Por lo tanto, si el orden de carga de las dependencias es importante para usted, establezca el atributo
asíncrono en
false
.
Transmitir menos
No voy a atacar a Babel aquí. Esta herramienta es necesaria en el desarrollo web moderno, pero es una entidad muy descarriada. Babel agrega muchas cosas al código que genera, que el desarrollador puede no conocer. Por lo tanto, no te arrepentirás si miras las entrañas de Babel y descubres exactamente lo que está haciendo. En particular, el conocimiento de los mecanismos internos de Babel deja en claro que pequeños cambios en la forma en que alguien escribe el código pueden tener un efecto positivo en lo que genera Babel.
Transmitir menosEs decir, de eso estamos hablando. Por ejemplo,
las opciones predeterminadas son una característica muy útil de ES6 que ya puede estar usando:
function logger(message, level = "log") { console[level](message); }
Aquí vale la pena prestar atención al parámetro de
level
, cuyo valor predeterminado es el
log
cadena. Esto significa que si queremos llamar a
console.log
usando la función de contenedor
logger
, entonces no necesitamos pasar de
level
a esta función. Conveniente, ¿verdad? Todo esto es bueno, excepto por el código que obtiene Babel al transformar esta función:
function logger(message) { var level = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "log"; console[level](message); }
Este es un ejemplo de cómo, a pesar del hecho de que somos guiados por buenas intenciones, las comodidades que brinda Babel pueden tener consecuencias negativas. Lo que eran solo unos pocos caracteres en el código fuente se convirtió en una construcción mucho más larga en la versión de producción del programa. - , ,
arguments
.
, ,
? , Babel :
, Babel
@babel/preset-env ,
. , , , , , ! — «» (
true
loose ). — , , , , , . «»
, Babel , .
, «» , , Babel :
, — JavaScript, , .
spread ,
,
.
— :
- — @babel/runtime @babel/plugin-transform-runtime , , Babel .
- , . @babel/polyfill . , babel /preset-env useBuiltins
usage
.
, , , , , . ,
JSX , , , . , , . , , . , Babel — . , Babel. , .
: —
, . , JavaScript-, , . , , . - .
. , , , , , , , .
, , , , , , , . - — . , , , . . , , , , . , , , .
Estimados lectores! JS-?
