¿Qué hacer cuando CSS bloquea el análisis de páginas?

Recientemente, realicé una auditoría de un sitio y encontré un patrón preload/polyfillque ya he visto con varios clientes. Hoy en día, no se recomienda el uso de este patrón anteriormente popular. Sin embargo, es útil considerarlo para ilustrar la importancia del uso cuidadoso del mecanismo de precarga de materiales por parte de los navegadores web. También es interesante porque le permite demostrar un ejemplo del mundo real de cómo el orden de los elementos en un documento puede afectar el rendimiento (esto es lo que se discute en este maravilloso artículo de Harry Roberts). El material, cuya traducción publicamos hoy, está dedicado al análisis de situaciones en las que el manejo inadecuado e inoportuno de los recursos CSS afecta el rendimiento de las páginas web.





Sobre loadCSS


Soy un gran admirador del Filament Group : lanzan una increíble cantidad de grandes proyectos. Además, constantemente crean herramientas invaluables y las comparten para mejorar la web. Una de estas herramientas es loadCSS , que durante mucho tiempo fue la herramienta que recomendé que todos usaran para cargar recursos CSS no críticos.

Aunque esto ahora ha cambiado (y el Filament Group ha publicado un excelente artículo sobre lo que sus empleados prefieren usar en estos días), todavía, cuando audito sitios, a menudo veo cómo lo loadCSSusan en la producción.

Uno de los patrones que he encontrado es el patrón.preload/polyfill. Con este enfoque, todos los archivos con estilos se cargan en modo de precarga (el atributo de los relenlaces correspondientes se establece en preload). Después de eso, cuando estén listos para usar, aplique sus eventos onloadpara conectarlos a la página.

<link rel="preload" href="path/to/mystylesheet.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="path/to/mystylesheet.css"></noscript>

Dado que no todos los navegadores admiten el diseño <a href="https://caniuse.com/#feat=link-rel-preload"><link rel="preload"></a>, el proyecto loadCSSofrece a los desarrolladores un polyfill conveniente, que se agrega a la página después de la descripción de los enlaces relevantes:

<link rel="preload" href="path/to/mystylesheet.css" as="style" onload="this.rel='stylesheet'">
<noscript>
    <link rel="stylesheet" href="path/to/mystylesheet.css">
</noscript>
<script>
/*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */
(function(){ ... }());
</script>

Desorden en las prioridades de la red


Nunca he sido un fanático ardiente de este patrón. La precarga es una herramienta grosera. Los materiales descargados mediante enlaces de atributos rel="preload"luchan con éxito con otros materiales por recursos de red. El uso preloadsupone que las hojas de estilo que se cargan de forma asíncrona debido al hecho de que no desempeñan un papel crítico en el resultado de una página reciben una prioridad muy alta de los navegadores.

La siguiente imagen, tomada de WebPageTest, demuestra muy bien este problema. En las líneas 3-6, puede ver la carga asincrónica de archivos CSS utilizando un patrón preload. Pero, aunque los desarrolladores consideran que estos archivos no son tan importantes que descargarlos bloquearía la representación, utilicepreload significa que se cargarán antes de que el navegador reciba los recursos restantes.


Los archivos CSS que usan el patrón de precarga cuando se cargan llegarán al navegador antes que otros recursos, a pesar de que no son recursos esenciales para la representación inicial de una página

Bloqueo del analizador HTML


Los problemas asociados con la prioridad de la carga de recursos ya son suficientes para evitar usar el patrón en la mayoría de las situaciones preload. Pero en este caso, la situación se ve agravada por la presencia de otra hoja de estilo, que se carga de la manera habitual.

<link rel="stylesheet" href="path/to/main.css" />
<link rel="preload" href="path/to/mystylesheet.css" as="style" onload="this.rel='stylesheet'">
<noscript>
    <link rel="stylesheet" href="path/to/mystylesheet.css">
</noscript>
<script>
/*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */
(function(){ ... }());
</script>

Existen los mismos problemas cuando se utiliza preloadel hecho de que no los archivos más importantes tienen alta prioridad. Pero es igual de importante, y quizás menos obvio, qué efecto tiene esto en la capacidad del navegador para analizar la página.

Una vez más, esto ya se ha escrito en detalle , por lo que recomiendo leer ese material para comprender mejor lo que está sucediendo. Aquí hablaré sobre esto brevemente.

Por lo general, cargar estilos bloquea la representación de la página. El navegador necesita consultar y analizar estilos para poder mostrar la página. Sin embargo, esto no impide que el navegador analice el resto del código HTML.

Los scripts, por otro lado, bloquean el analizador si no están marcados como defero async.

Dado que el navegador debe suponer que el script puede manipular el contenido de la página o los estilos aplicados, debe tener cuidado cuando se ejecuta este script. Si el navegador sabe que se está cargando algún código CSS, esperará la llegada de este código CSS y luego ejecutará el script. Y dado que el navegador no puede continuar analizando el documento hasta que se ejecute el script, esto significa que los estilos ya no solo bloquean la representación. Evitan que el navegador analice HTML.

Este comportamiento es cierto tanto para los scripts externos como para los scripts incrustados en la página. Si se carga CSS, los scripts en línea no se ejecutan hasta que este CSS llega al navegador.

Estudio de problemas


La forma más comprensible de visualizar este problema es usar las herramientas de desarrollador de Chrome (realmente me gusta el nivel al que han crecido estas herramientas).

Entre las herramientas de Chrome hay una pestaña Performancecon la que puede grabar el perfil de carga de la página. Recomiendo ralentizar artificialmente la conexión de red para que el problema se manifieste más brillante.

En este caso, probé usando la configuración de red Fast 3G. Si observa detenidamente lo que sucede con el hilo principal, puede comprender que la solicitud para cargar el archivo CSS se produce al comienzo del análisis HTML (aproximadamente 1,7 segundos después de que la página comienza a cargarse).


Un pequeño rectángulo debajo del bloque de análisis HTML representa una solicitud para recibir un archivo CSS.

Durante el siguiente período de tiempo, que es aproximadamente un segundo, el hilo principal está inactivo. Aquí puedes ver pequeñas islas de actividad. Este es un desencadenante de eventos que indican la finalización de la carga de estilos; es el envío por el mecanismo de precarga de recursos de otras solicitudes. Pero el navegador deja de analizar HTML por completo.


Si observa el panorama general, resulta que después del inicio de la carga de CSS, la transmisión principal está inactiva durante más de 1.1 segundos.

Por lo tanto, han pasado 2.8 segundos, el estilo se carga y el navegador lo procesa. Solo entonces vemos el procesamiento de la secuencia de comandos incorporada, y después de eso, el navegador finalmente vuelve al análisis HTML.


CSS llega en aproximadamente 2.8 segundos, después de lo cual vemos que el navegador continúa analizando HTML

Firefox es una buena excepción


El comportamiento anterior es común para Chrome, Edge y Safari. Firefox es una buena excepción a la lista de navegadores populares.

Todos los demás navegadores suspenden el análisis HTML, pero utilizan un analizador proactivo (un medio para precargar materiales) para ver el código de los enlaces a recursos externos y cumplir con las solicitudes de carga de estos recursos. Firefox, sin embargo, va un paso más allá en este asunto: construye especulativamente un árbol DOM, aunque espera que el script se ejecute.

A menos que el script manipule el DOM, lo que lleva a la necesidad de descartar los resultados del análisis especulativo, este enfoque permite que Firefox se aproveche. Por supuesto, si el navegador tiene que soltar el árbol DOM construido especulativamente, significa que no hizo nada útil al construir este árbol.

Este es un enfoque interesante. Tenía mucha curiosidad por saber qué tan efectivo es. Ahora, sin embargo, no hay información sobre esto en Firefox Performance Profiler. Allí no puede averiguar si el analizador especulativo funcionó, si es necesario rehacer el trabajo realizado y si aún necesita ser rehecho, cómo esto afectará el rendimiento.

Hablé con los responsables de las herramientas para desarrolladores de Firefox, y puedo decir que tienen ideas interesantes sobre cómo presentar dicha información en el generador de perfiles en el futuro. Espero que tengan éxito.

Solución al problema


En el caso del cliente, que mencioné al principio, el primer paso para resolver este problema parecía extremadamente simple: deshacerse del patrón preload/polyfill. La precarga de CSS no crítico no tiene sentido. Aquí debe cambiar a usar, en lugar de rel="preload", un atributo media="print". Esto es exactamente lo que recomiendan los expertos del Grupo Filamento. Este enfoque, además, le permite deshacerse por completo de polyfill.

<link rel="stylesheet" href="/path/to/my.css" media="print" onload="this.media='all'">

Esto ya nos coloca en una mejor posición: ahora las prioridades de la red están mucho mejor alineadas con la importancia real de las descargas. Y también nos deshacemos del script incorporado.

En este caso, todavía hay otro script incorporado ubicado en el encabezado del documento, debajo de la línea que inicia la solicitud para cargar CSS. Si mueve este script de modo que esté delante de la línea que carga el CSS, esto eliminará el bloqueo del analizador. Si analiza la página nuevamente utilizando las Herramientas para desarrolladores de Chrome, la diferencia será completamente obvia.


Antes de realizar cambios en el código de la página, el analizador HTML se detuvo en la línea 1939, después de haber encontrado un script incorporado, y permaneció aquí durante aproximadamente un segundo. Después de la optimización, pudo llegar a la línea 5281

Anteriormente, el analizador se detuvo en la línea 1939 y estaba esperando que se cargara CSS, pero ahora llega a la línea 5281. Allí, al final de la página, hay otro script incorporado que detiene el analizador nuevamente.

Esta es una solución rápida al problema. Esta no es la opción que representa la solución final al problema. Cambiar el orden de los elementos y deshacerse del patrónpreload/polyfilles solo el primer paso. Puede resolver mejor este problema incrustando código CSS crítico en una página, en lugar de cargarlo desde un archivo externo. Modelopreload/polyfillDiseñado para ser utilizado además de CSS en línea. Esto nos permite ignorar por completo los problemas asociados con los scripts y asegurarnos de que el navegador, después de ejecutar la primera solicitud, tenga todos los estilos necesarios para representar la página.

Pero por ahora, debe tenerse en cuenta que podemos lograr un buen aumento del rendimiento haciendo cambios muy pequeños en el proyecto con respecto a la forma en que se cargan los estilos y el orden de los elementos en el DOM.

Resumen


  • Si usa un loadCSSpatrón preload/polyfill, vaya al patrón de carga de estilo print.
  • Si tiene estilos externos que se cargan de la manera habitual (es decir, utilizando enlaces regulares a archivos de estos estilos), mueva todos los scripts incorporados que se pueden mover sobre los enlaces para cargar estilos.
  • Incruste estilos críticos en la página para garantizar el inicio más rápido posible del renderizado.

¡Queridos lectores! ¿Ha encontrado problemas para ralentizar el renderizado de páginas debido a CSS?

¡PS
RUVDS felicita a todos los profesionales de TI el 8 de marzo!
Este año, decidimos no dar tulipanes y no hacer una selección de regalos geek. Tomamos un camino diferente y creamos la página IT is female para mostrar la presencia de especialistas femeninas en IT.


All Articles