En los últimos años, lo que se llama el "
precio de JavaScript " ha visto grandes cambios positivos debido a la mayor velocidad de análisis y la compilación de scripts en el navegador. Ahora, en 2019, los componentes principales de la carga en los sistemas creados por JavaScript son el tiempo de carga de los scripts y su tiempo de ejecución.

La interacción del usuario con el sitio puede verse interrumpida temporalmente si el navegador está ocupado ejecutando código JavaScript. Como resultado, podemos decir que la optimización de los cuellos de botella asociados con la carga y ejecución de scripts puede tener un fuerte impacto positivo en el rendimiento del sitio web.
Pautas prácticas generales para la optimización del sitio web
¿Qué significa lo anterior para los desarrolladores web? El punto aquí es que el costo de los recursos para analizar (analizar, analizar) y compilar scripts no es tan grave como antes. Por lo tanto, al analizar y optimizar paquetes de JavaScript, los desarrolladores deben considerar las siguientes tres recomendaciones:
- Intente reducir el tiempo requerido para descargar scripts.
- Intenta mantener tus paquetes JS pequeños. Esto es especialmente importante para los sitios diseñados para dispositivos móviles. El uso de paquetes pequeños mejora el tiempo de carga del código, reduce el nivel de uso de memoria y reduce la carga en el procesador.
- Intente evitar que todo el código del proyecto se presente como un gran paquete. Si el tamaño del paquete excede aproximadamente 50-100 Kb, divídalo en fragmentos separados de un tamaño pequeño. Gracias a la multiplexación HTTP / 2, se pueden procesar simultáneamente múltiples solicitudes de servidor y múltiples respuestas. Esto reduce la carga en el sistema asociada con la necesidad de cumplir con solicitudes adicionales de carga de datos.
- Si está trabajando en un proyecto móvil, intente mantener el código lo más pequeño posible. Esta recomendación está asociada con bajas tasas de datos a través de redes móviles. Además, luche por el uso económico de la memoria.
- Intente reducir el tiempo requerido para ejecutar scripts.
- Evite usar tareas largas que puedan cargar el hilo principal durante mucho tiempo y aumente el tiempo que tardan las páginas en estar en un estado en el que los usuarios puedan interactuar con ellas. En el entorno actual, las secuencias de comandos que se ejecutan después de cargarse contribuyen de manera importante al "precio de JavaScript".
- No incruste fragmentos de código grandes en las páginas.
- Aquí se debe cumplir la siguiente regla: si el tamaño del script supera 1 Kb, intente no incrustarlo en el código de la página. Una de las razones de esta recomendación es el hecho de que 1 Kb es el límite después del cual el almacenamiento en caché del código de script externo comienza a funcionar en Chrome. Además, tenga en cuenta que el análisis y la compilación de scripts incrustados todavía se está ejecutando en el hilo principal.
¿Por qué es tan importante cargar y ejecutar scripts?
¿Por qué es importante optimizar el tiempo de carga y ejecución de los scripts en las condiciones modernas? Los tiempos de carga de scripts son extremadamente importantes en situaciones donde se accede a los sitios a través de redes lentas. A pesar del hecho de que las redes 4G (e incluso 5G) se están extendiendo cada vez más, la propiedad
NetworkInformation.effectiveType en muchos casos de usar conexiones de Internet móvil muestra indicadores que están en el nivel de las redes 3G o incluso en niveles más bajos.
El tiempo requerido para ejecutar el código JS es importante para dispositivos móviles con procesadores lentos. Debido al hecho de que los dispositivos móviles usan diferentes CPU y GPU, debido al hecho de que cuando los dispositivos se sobrecalientan, para protegerlos, el rendimiento de sus componentes disminuye, se puede observar una brecha grave entre el rendimiento de teléfonos y tabletas caros y baratos. Esto afecta en gran medida el rendimiento del código JavaScript, ya que la capacidad de ejecutar dicho código por un dispositivo está limitada por las capacidades del procesador de este dispositivo.
De hecho, si analizamos el tiempo total dedicado a cargar y preparar la página para trabajar en un navegador como Chrome, aproximadamente el 30% de este tiempo puede gastarse ejecutando código JS. A continuación se muestra un análisis de la carga de una página web muy típica (reddit.com) en una computadora de escritorio de alto rendimiento.
En el proceso de carga de la página, aproximadamente el 10-30% del tiempo se dedica a la ejecución de código usando V8Si hablamos de dispositivos móviles, en un teléfono promedio (Moto G4) se tarda 3-4 veces más en ejecutar reddit.com en un código JS que en un dispositivo de alto nivel (Pixel 3). En un dispositivo débil (Alcatel 1X cuesta menos de $ 100), resolver el mismo problema requiere al menos 6 veces más tiempo que algo como Pixel 3.
El tiempo requerido para procesar el código JS en dispositivos móviles de diferentes clases.Tenga en cuenta que las versiones móvil y de escritorio de reddit.com son diferentes. Por lo tanto, no puede comparar los resultados de dispositivos móviles y, por ejemplo, MacBook Pro.
Cuando intente optimizar el tiempo de ejecución del código JavaScript, preste atención a las
tareas largas que pueden capturar la transmisión de la interfaz de usuario durante mucho tiempo. Estas tareas pueden impedir el cumplimiento de otras tareas extremadamente importantes, incluso cuando la página parece completamente lista para funcionar. Las tareas a largo plazo deben dividirse en tareas más pequeñas. Al dividir el código en partes y controlar el orden de carga de estas partes, puede lograr el hecho de que las páginas llegarán a un estado interactivo más rápido. Esto, con suerte, llevará a los usuarios a tener menos inconvenientes para interactuar con las páginas.
Las tareas de larga duración capturan el hilo principal. Deben romperse en pedazos¿Cómo aceleran las mejoras de V8 el análisis y la compilación de scripts?
La velocidad de analizar el código fuente JS en V8, desde la época de Chrome 60, ha aumentado en 2 veces. Al mismo tiempo, el análisis y la compilación ahora contribuyen menos al "precio de JavaScript". Esto es gracias a otros esfuerzos de optimización de Chrome que llevan a la paralelización de estas tareas.
En V8, la cantidad de trabajo en el análisis y compilación de código producido en el hilo principal se reduce en un promedio del 40%. Por ejemplo, para Facebook, la mejora de este indicador fue del 46%, para Pinterest: 62%. El resultado más alto, 81%, se obtuvo para YouTube. Tales resultados son posibles debido al hecho de que el análisis y la compilación se mueven a una secuencia separada. Y esto se suma a las mejoras existentes con respecto a la solución de transmisión de las mismas tareas fuera de la transmisión principal.
Tiempo de análisis JS en varias versiones de ChromeTambién puede visualizar cómo las optimizaciones V8 producidas en varias versiones de Chrome afectan el tiempo del procesador requerido para procesar el código. Al mismo tiempo que Chrome 61 necesitaba analizar el código JS de Facebook, Chrome 75 ahora puede analizar el código JS de Facebook y, además, analizar el código de Twitter 6 veces.
En el momento en que Chrome 61 necesitaba procesar el código JS de Facebook, Chrome 75 puede procesar tanto el código de Facebook como seis veces la cantidad de código de Twitter.Hablemos de cómo se lograron tales mejoras. En pocas palabras, los recursos del script se pueden analizar y compilar en modo de transmisión en el flujo de trabajo. Esto significa lo siguiente:
- V8 puede analizar y compilar código JS sin bloquear el hilo principal.
- El procesamiento continuo del script comienza cuando el analizador HTML universal encuentra la
<script>
. Un analizador HTML maneja los scripts que bloquean el análisis de páginas. Encontrándose con guiones asíncronos, él continúa trabajando. - En la mayoría de los escenarios del mundo real, caracterizados por ciertas velocidades de conexión de red, el V8 analiza el código más rápido de lo que puede cargar. Como resultado, V8 completa las tareas de analizar y compilar el código unos milisegundos después de cargar los últimos bytes del script.
Si habla de todo esto con un poco más de detalle, entonces el punto aquí es el siguiente. En versiones mucho más antiguas de Chrome, el script necesitaba ser descargado en su totalidad antes de analizarlo. Este enfoque es simple y comprensible, pero cuando se usa, los recursos del procesador se usan irracionalmente. Chrome, entre las versiones 41 y 68, comienza a analizar en modo asíncrono, inmediatamente después de que el script comienza a cargar, realizando esta tarea en un hilo separado.
Las secuencias de comandos se envían al navegador en fragmentos. V8 comienza a procesar el procesamiento de datos después de que tiene al menos 30 Kb de código.En Chrome 71, pasamos a un sistema basado en tareas. Aquí, el planificador puede iniciar simultáneamente varias sesiones de procesamiento de scripts asíncronos / retardados. Debido a este cambio, la carga creada al analizar el hilo principal ha disminuido en aproximadamente un 20%. Esto condujo a una mejora de aproximadamente 2% en los puntajes de TTI / FID obtenidos en sitios reales.
Chrome 71 utiliza un sistema de procesamiento de código basado en tareas. Con este enfoque, el planificador puede procesar varios scripts asíncronos / pendientes al mismo tiempo.En Chrome 72, hicimos que el procesamiento de transmisión sea la forma principal de analizar los scripts. Ahora, incluso los scripts síncronos regulares se manejan de esta manera (aunque esto no se aplica a los scripts integrados). Además, dejamos de cancelar las operaciones de análisis basadas en tareas si el hilo principal necesitaba un código analizado. Esto se hace debido al hecho de que esto lleva a la necesidad de volver a realizar parte del trabajo ya realizado.
La versión anterior de Chrome tenía soporte para análisis de transmisión y compilación de código. Luego, el script descargado de la red primero debe ingresar a la transmisión principal, y luego se redirigirá al sistema de procesamiento de secuencias de comandos de transmisión.
Esto a menudo llevó al analizador de flujo a esperar datos que ya se descargaron de la red pero que el flujo principal aún no ha redirigido al procesamiento de flujo. Esto sucedió debido al hecho de que el hilo principal podría estar ocupado con otras tareas (como analizar HTML, crear un diseño de página o ejecutar código JS).
Ahora estamos experimentando cómo comenzar a analizar el código al precargar páginas. Anteriormente, la implementación de dicho mecanismo se veía obstaculizada por la necesidad de utilizar los recursos del hilo principal para transferir tareas al analizador de transmisión. Los detalles sobre el análisis del código JS que se ejecuta "instantáneamente" se pueden encontrar
aquí .
¿Cómo han afectado las mejoras a lo que se puede ver en las herramientas del desarrollador?
Además de lo anterior, se puede observar que antes había un
problema en las herramientas de desarrollador. Consistió en el hecho de que la información sobre la tarea de análisis se mostraba como si bloquearan completamente el hilo principal. Sin embargo, el analizador realizó operaciones bloqueando el hilo principal solo cuando necesitaba nuevos datos. Desde que pasamos del esquema de usar una sola secuencia para el procesamiento de datos de transmisión al esquema en el que se aplican las tareas de procesamiento de transmisión, esto se ha vuelto bastante obvio. Esto es lo que puedes ver en Chrome 69.
El problema está en las herramientas del desarrollador, debido a que la información sobre los scripts de análisis se muestra como si bloquearan completamente el hilo principalAquí puede ver que la tarea Parse Script tarda 1.08 segundos. ¡Pero analizar JavaScript, de hecho, no es tan lento! La mayor parte de este tiempo, no se realiza nada útil excepto esperar datos del hilo principal.
En Chrome 76, ya puedes ver una imagen completamente diferente.
En Chrome 76, el análisis se divide en muchas tareas pequeñasEn general, se puede observar que la pestaña Rendimiento de las herramientas del desarrollador es excelente para ver la imagen general de lo que está sucediendo en la página. Para obtener información más detallada que refleje las características de V8, como el tiempo de análisis y el tiempo de compilación, puede usar el Rastreo de Chrome con soporte RCS (Estadísticas de llamadas en tiempo de ejecución). En los datos RCS recibidos, puede encontrar los indicadores Parse-Background y Compile-Background. Pueden informar cuánto tiempo les llevó analizar y compilar el código JS fuera del hilo principal. Las métricas de análisis y compilación indican cuánto tiempo se ha dedicado a actividades relacionadas en el hilo principal.
Análisis de datos RCS usando Google Tracing¿Cómo afectaron los cambios al trabajo con sitios reales?
Veamos algunos ejemplos de cómo el procesamiento de secuencias de comandos de transmisión influyó en la exploración de sitios reales.
▍Reddit
Ver reddit.com en una MacBook Pro. Tiempo para analizar y compilar el código JS empleado en los subprocesos principales y de trabajoHay varios paquetes JS en el sitio web reddit.com, cada uno de los cuales supera el tamaño de 100 Kb. Están envueltos en funciones externas, lo que lleva a la ejecución de grandes volúmenes de
compilación "perezosa" en el hilo principal. En el diagrama anterior, es crucial el tiempo requerido para procesar los scripts en el hilo principal. Esto se debe al hecho de que una carga grande en el hilo principal puede aumentar el tiempo que tarda la página en cambiar al modo interactivo. Al procesar el código del sitio reddit.com, la mayor parte del tiempo se pasa en el hilo principal, y los recursos del trabajo / hilo de fondo se usan al mínimo.
Puede optimizar este sitio dividiendo algunos paquetes grandes en partes (aproximadamente 50 Kb cada uno) y sin envolver el código en una función. Esto maximizaría el procesamiento paralelo de scripts. Como resultado, los paquetes podrían analizarse y compilarse al mismo tiempo en modo de transmisión. Esto reduciría la carga en el hilo principal al preparar la página para el trabajo.
▍Facebook
Ver facebook.com en tu MacBook Pro. Tiempo para analizar y compilar el código JS empleado en los subprocesos principales y de trabajoTambién podemos considerar un sitio como facebook.com, que usa alrededor de 6 MB de código JS comprimido. Este código se carga utilizando aproximadamente 292 solicitudes. Algunos de ellos son asíncronos, algunos están destinados a precargar datos, otros tienen baja prioridad. La mayoría de los scripts de Facebook son pequeños y tienen un enfoque limitado. Esto puede tener un buen efecto en el procesamiento de datos en paralelo por medio de flujos de fondo / trabajo. El hecho es que muchas secuencias de comandos pequeñas se pueden analizar y compilar al mismo tiempo mediante el procesamiento de secuencias de comandos de transmisión.
Tenga en cuenta que su sitio es probablemente diferente del sitio de Facebook. Probablemente no tenga aplicaciones que se mantengan abiertas durante mucho tiempo (como lo que son un sitio de Facebook o la interfaz de Gmail), y cuando trabaje con ellas, puede estar justificado descargar volúmenes tan serios de scripts con un navegador de escritorio. Pero, a pesar de esto, podemos dar una recomendación general que sea justa para cualquier proyecto. Se basa en el hecho de que vale la pena dividir el código de la aplicación en paquetes modestos, y que necesita descargar estos paquetes solo cuando sea necesario.
Aunque la mayor parte del trabajo de análisis y compilación de código JS se puede realizar utilizando herramientas de transmisión en un subproceso en segundo plano, algunas operaciones aún requieren un subproceso principal. Cuando el hilo principal está ocupado con algo, la página no puede responder a la interacción del usuario. Por lo tanto, se recomienda prestar atención al impacto en los sitios UX que tiene la carga y ejecución del código JS.
Tenga en cuenta que no todos los motores y navegadores de JavaScript ahora transmiten secuencias de comandos y optimizan su carga. Pero a pesar de esto, esperamos que los principios generales de optimización descritos anteriormente puedan mejorar la experiencia del usuario al trabajar con sitios visitados en cualquiera de los navegadores existentes.
Precio de análisis JSON
Analizar el código JSON puede ser mucho más eficiente que analizar el código JavaScript. La cuestión es que la gramática JSON es mucho más simple que la gramática de JavaScript. Este conocimiento se puede aplicar para mejorar la velocidad de preparación para el trabajo de aplicaciones web que utilizan objetos de configuración grandes (como los repositorios de Redux), cuya estructura se asemeja al código JSON. Como resultado, resulta que en lugar de presentar los datos como literales de objetos incrustados en el código, puede representarlos como cadenas de objetos JSON y analizar estos objetos en tiempo de ejecución.
El primer enfoque, usando objetos JS, se ve así:
const data = { foo: 42, bar: 1337 };
El segundo enfoque, utilizando cadenas JSON, implica el uso de tales construcciones:
const data = JSON.parse('{"foo":42,"bar":1337}');
Dado que solo necesita ejecutar el procesamiento de cadenas JSON una vez, el enfoque que usa
JSON.parse
es mucho más rápido que usar literales de objetos JavaScript. Especialmente - al cargar la página "en frío". Se recomienda que use cadenas JSON para representar objetos que comienzan en 10 Kb. Sin embargo, como con cualquier consejo de rendimiento, este consejo no debe seguirse sin pensar. Antes de aplicar esta técnica para presentar datos en producción, es necesario realizar mediciones y evaluar su impacto real en el proyecto.
El uso de literales de objetos como almacenamiento para grandes cantidades de datos plantea otra amenaza. El punto es que existe el riesgo de que tales literales se puedan procesar dos veces:
- El primer paso de procesamiento se realiza con el análisis preliminar del literal.
- El segundo enfoque se realiza durante el análisis "perezoso" del literal.
No puede deshacerse del primer paso de procesamiento de literales de objeto. Pero, afortunadamente, el segundo paso se puede evitar colocando literales de objetos en el nivel superior o dentro de
PIFE .
¿Qué pasa con el análisis y la compilación de código en visitas repetidas a sitios?
Es posible optimizar el rendimiento del sitio para aquellos casos en que los usuarios los visitan más de una vez, gracias a las capacidades de V8 para el almacenamiento en caché de código y bytecode. Cuando se solicita un script del servidor por primera vez, Chrome lo descarga y pasa el V8 para su compilación. El navegador, además, guarda el archivo de este script en su caché de disco. Cuando se ejecuta la segunda solicitud para descargar el mismo archivo JS, Chrome lo toma de la memoria caché del navegador y nuevamente pasa el V8 para su compilación. Esta vez, sin embargo, el código compilado se serializa y se adjunta al archivo de script en caché como metadatos.
Sistema de almacenamiento en caché de código en V8Cuando se solicita el script por tercera vez, Chrome toma tanto el archivo como sus metadatos del caché y luego transfiere el V8. V8 deserializa los metadatos y, como resultado, puede omitir el paso de compilación. El almacenamiento en caché de código se activa si las visitas al sitio se realizan dentro de las 72 horas. Chrome también utiliza la estrategia de almacenamiento en caché codicioso de código cuando se utiliza un trabajador de servicio para almacenar en caché los scripts. Los detalles sobre el almacenamiento en caché de código se pueden encontrar
aquí .
Resumen
En 2019, los principales cuellos de botella de rendimiento para las páginas web son cargar y ejecutar scripts. Para mejorar la situación, trate de utilizar scripts síncronos (integrados) de tamaños pequeños, que son necesarios para organizar la interacción del usuario con esa parte de la página que es visible para él inmediatamente después de la carga. Se recomienda que los scripts utilizados para dar servicio a otras partes de las páginas se carguen en modo diferido. Rompa los paquetes grandes en pedazos pequeños. Esto facilitará la implementación de una estrategia para trabajar con código, en la aplicación de la cual el código se carga solo cuando es necesario y solo donde es necesario. Esto maximizará las capacidades de V8, dirigido al procesamiento paralelo del código.
Si está desarrollando proyectos móviles, debe esforzarse por asegurarse de que usen el menor código JS posible. Esta recomendación se deriva del hecho de que los dispositivos móviles generalmente funcionan en redes bastante lentas. Dichos dispositivos, además, pueden estar limitados en términos de RAM disponible y recursos de procesador disponibles. Intente encontrar un equilibrio entre el tiempo requerido para preparar los scripts descargados de la red y el uso de la memoria caché. Esto maximizará la cantidad de análisis y compilación de código realizado fuera del hilo principal.
Estimados lectores! ¿Optimiza sus proyectos web teniendo en cuenta las peculiaridades del procesamiento del código JS por parte de los navegadores modernos?
