Amigos, hoy no hablaré de tonterías, solo contaré un gran problema que encontré en estos dos días y cómo lo resolví. La cosa es así: tenemos un proyecto antiguo en línea que siempre funcionó bien, pero recientemente los usuarios reportaron que la página se ponía en blanco de vez en cuando. La tasa de errores no era alta, pero debido al gran número de usuarios, todos los días recibía montones de errores en mi correo. Al principio pensé que era un problema del backend, pero después de investigar no encontré nada. Luego miré el monitoreo del frontend y me quedé atónito: todos eran errores de JS, y todos estaban concentrados en el archivo bundle comprimido.
Como saben, el JS en producción está comprimido, con líneas de decenas de miles de caracteres, y los mensajes de error son ilegibles. Miré ese 'Unexpected token' durante mucho tiempo, sintiendo que se burlaba de mí. Finalmente, activé los sourcemaps para localizar el problema y descubrí que los errores eran variados, pero todos tenían algo en común: ocurrían en código dentro de condicionales o que usaba sintaxis nueva de ES6+.
Fue entonces cuando me di cuenta de que el problema podría estar en la configuración de compresión. Usamos una herramienta de compresión bastante antigua, y en su momento, por conveniencia, usamos la configuración predeterminada sin revisarla a fondo. Algunas opciones de optimización en la configuración predeterminada, como 'drop_debugger' y las cosas raras en 'compress', son un desastre para código antiguo.
Te doy el ejemplo más típico: la herramienta de compresión elimina por defecto ramas condicionales que considera 'siempre falsas'. Por ejemplo, si escribes if (typeof window !== 'undefined') en tu código para compatibilidad con SSR o para evitar errores en entornos especiales, el compresor piensa que esa condición es innecesaria porque analiza el contexto y asume que window existe, y elimina todo el bloque if. ¿El resultado? En dispositivos reales, en algunos entornos WebView, ciertas propiedades de window no son accesibles y el código colapsa.
Otro problema más grave: el compresor acorta los nombres de variables dentro de las funciones, por ejemplo, convierte userName en a y orderList en b. Esto no causa problemas la mayoría de las veces, pero si usas eval o new Function en tu código y haces referencia a variables externas, después de la compresión los nombres no coinciden y obtienes un ReferenceError. Tenemos varios módulos antiguos en línea que usan esto; normalmente funcionan, pero al comprimirlos se rompen.
Así que en estos dos días, decidí tomármelo en serio y dediqué una tarde entera a investigar la configuración de compresión. ¿Adivina qué pasó después de cambiarla? ¡Los errores en línea se redujeron en un 80%! No es una tontería, era que la configuración no estaba bien.
Específicamente, cambié varias cosas; tomen nota, quizás algún día les salve la vida:
Primero, desactivé conditionals y dead_code en la opción compress. Esto evita que el compresor elimine ramas de código por su cuenta. Aunque la tasa de compresión baje un poco, tal vez aumente unos KB, pero a cambio se gana estabilidad, ¡vale la pena! Especialmente para proyectos antiguos llenos de comprobaciones de compatibilidad, estas dos opciones son bombas de tiempo.
Segundo, configuré eval en mangle como true. Esto significa que si se detecta eval o new Function en el código, no se modifiquen los nombres de variables dentro de ellos, o simplemente se conserven las variables sin acortar. Aunque la compresión sea menos efectiva, es mejor que tener errores en producción.
Tercero, también desactivé unused en compress. Esta opción elimina parámetros que están 'definidos pero no usados'. Pero a veces dejamos un parámetro como marcador de posición a propósito, por ejemplo, en una función de callback, el tercer parámetro es el útil y los dos primeros son parte de la firma fija. El compresor no lo sabe; ve que los dos primeros no se usan y los elimina, causando que los parámetros del callback se desalineen y al llamar la función se obtenga undefined, rompiendo toda la lógica.
Cuarto, y lo más fácil de pasar por alto, es la configuración ascii_only en output. Si tu código contiene cadenas en chino o emojis, te recomiendo configurarla como false; de lo contrario, todo se convierte en secuencias de escape como \uXXXX. Aunque la funcionalidad es la misma, la longitud de las cadenas se dispara, y algunos navegadores antiguos tienen problemas al parsear cadenas muy largas, lo que puede causar congelamientos o errores.
En resumen, mi configuración actual es el modo 'no te metas en lo que no te importa'. El propósito de la compresión es reducir el tamaño, pero si rompes la funcionalidad para reducir el tamaño, el tráfico ahorrado no paga las horas extra. Calculé que después de cambiar la configuración, el tamaño del archivo empaquetado solo aumentó alrededor del 15%, pero la tasa de errores en línea bajó de cientos por día a solo unos pocos. Ese aumento de tamaño no es nada.
Por último, un consejo: después de cambiar la configuración de compresión, asegúrate de ejecutar una prueba de regresión completa, especialmente en las partes que usan carga perezosa de rutas o import dinámico. No me pregunten por qué lo sé; son muchas lágrimas. Ahora que la producción está estable, puedo dormir bien. Si ustedes tienen problemas similares, no duden en culpar a la lógica del código; primero revisen si su configuración de compresión está haciendo más daño que bien.