soy Kseso y esto EsCSS

Análisis Css para divertirse y sacar provecho. Apuntes de optimización

Análisis Css para divertirse y sacar provecho. Apuntes de optimización

✎ 0
COLABORACIÓN AUTOR INVITADO

Análisis Css para divertirse y sacar provecho. Apuntes de optimización

Recientemente he estado trabajando en optimizar el rendimiento de una aplicación web de una sola página. La aplicación era muy dinámica, interactiva y llena del nuevo CSS3. No estoy hablando sólo de esquinas redondeadas y gradientes. Está sembrada de sombras, gradientes, transformaciones, espolvoreada con transiciones suaves, colores semi-transparentes, trucos con pseudoelementoy y características experimentales CSS.

Aparte de mirar los cuellos de botella en el Javascript/DOM lado, me decidí a entrar en la tierra de CSS. Quería ver el tipo de impacto que estos elementos de la interfaz de usuario (UI) tienen en el rendimiento. La versión anterior de la aplicación - la que no tiene toda la pelusa - era mucho más ágil, a pesar de que la parte de JS no lo haya cambiado tan radicalmente. Pude ver que las cosas no eran tan rápido como deberían ser.

¿Tenían la culpa los estilos?

Afortunadamente, sólo unos pocos días antes, la gente de Opera había sacado un experimental analizador de estilos (seguido de un parche poco después). El inspector estaba destinado a revelar el desempeño de los selectores CSS, el reflujo del documento, su dibujado, e incluso análisis de los documentos y tiempos de carga del CSS.

¡Perfecto!

inspector css

Yo no estaba muy contento con inspeccionar y optimizar en un solo entorno de acuerdo con un motor (especialmente si el motor se utiliza sólo en un navegador), pero decidí darle una oportunidad. Después de todo, el comportamiento de los estilos/reglas culpables sea similar en todos los motores/navegadores. Y esto era prácticamente lo único que tenía.

La única herramienta similar era en WebKit la "timeline" en la pestaña "Herramientas de desarrollo". Pero la "línea de tiempo" no era muy amable para trabajar. No mostrará el tiempo correspondiente al flujo/dibujado/aplicación del selector, y la única forma de extraer esa información era por la exportación de datos como con JSON y analizar de forma manual (Voy a eso más adelante).

A continuación se presentan algunas de mis observaciones de utilizando tanto las herramientas de WebKit y Opera. La versión TL;DR está al final.

Antes de empezar, me gustaría mencionar que la mayoría (si no todas) de estas notas aplican mejor a los desarrollo grandes y complejos. Los documentos que tienen miles de elementos y que son altamente interactivos, los más beneficiados. En mi caso, he reducido el tiempo de carga de la página en ~650ms (~500 ms (!) solo en los estilos, ~100ms al renderizarla, y ~50 ms en flujo). La aplicación se hizo notablemente más ágil, sobre todo en los navegadores antiguos como Internet Explorer 7.

Para páginas/apps simples, hay un montón de otras optimizaciones que se debe mirar en primer lugar.

Notas

  1. La regla más rápida es la que no existe. Hay una estrategia común para combinar hojas de estilo de "módulos" en un solo archivo en la producción final. Esto hace que en una gran colección de reglas, algunas (muchas) de ellas probablemente no sean utilizadas en una parte específica del sitio/aplicación. Deshacerse de las reglas no utilizadas es una de las mejores cosas que puedes hacer para optimizar el rendimiento de CSS, ya que hay menos trabajo que hacer en primer lugar. Hay ciertos beneficios de tener un archivo grande, por supuesto, tales como la reducción del número de peticiones. Pero debería ser posible optimizar al menos las partes críticas de la aplicación, incluyendo sólo los estilos pertinentes.

    Este no es un descubrimiento nuevo de todas formas. Page Speed ​​siempre ha advertido sobre ello. Sin embargo, yo estaba muy sorprendido de ver realmente lo mucho que esto podría afectar al tiempo de procesamiento. En mi caso, me ahorré ~200-300ms por la aplicación de selectores -según el análisis perfiles de Opera- sólo mediante la eliminación de las reglas CSS sin usar. El tiempo en estructurar y dibujar la página bajó también.

  2. La reducción de las cargas -otra optimización conocida- juega un papel importante aquí. Estilos complejos no son tan caros cuanto menos rectificados/redibujado tenga que llevar a cabo el navegador. E incluso los estilos simples podrían ralentizar las cosas si se los aplica mucho. Reducir las redundancias y reducir la complejidad del CSS van de la mano.

  3. Los selectores que más penalizan tienden a ser los universales *, y aquellos con múltiples clases . foo.bar , foo. bar.baz qux , etc. Ya lo sabía, pero es bueno obtener la confirmación por los analizadores.

  4. Ten cuidado con los selectores universales * que se utilizan "sin razón alguna". Encontré selectores como buttton > *, pese a que dentro de los botones del sitio/aplicación sólo había <span>'s en ellos. Sustituir buttton > * por button > span logró algunas mejoras sorprendentes en el rendimiento selector. El navegador ya no tiene que emparejar cada uno de los elementos (por la regla de "derecha a izquierda"). Sólo tiene verificar si los <span>'s -cuyo número podría ser significativamente menor- y comprobar si el elemento padre es <button>. Obviamente, se necesita tener cuidado al sustituir * con etiquetas específicas, ya que a menudo es difícil de encontrar todos los lugares en los que se podría utilizar este selector.

    El gran inconveniente de esta optimización es que se pierde la flexibilidad, ya que al cambiar ahora el etiquetado será necesario cambiar el CSS también. No podría en el futuro reemplazar un botón con otro. Tenía mis dudas con esta sustitución, ya que esencialmente es deshacerse de una abstracción útil por el bien de rendimiento. Como siempre, tiene que encontrar el equilibrio adecuado para su caso particular, hasta que los motores comiencen a optimizar dichos selectores y no tenga que preocuparse por ellos.

  5. He utilizado este código para encontrar rápidamente los elementos buttton > * a sustituir. $$(selector).pluck('tagName').uniq(); // ["SPAN"] Se basa en las extensiones Array#pluck y Array#uniq de Prototype.js. Para la versión normal (dependientes de ES5 y selectores API), tal vez algo como esto funcione: Object.keys([].slice.call( document.querySelectorAll('button > *')) .reduce(function(memo, el){ memo[el.tagName] = 1; return memo; }, {}));
  6. Tanto en Opera como en Webkit, los selectores del tipo [type="..."] penalizan más que input[type="..."]. Probablemente ambos navegadores limiten el chequeo al los elementos con la etiqueta especidicada (después de todo el selector [type="..."] ES un selector universal).

  7. En Opera, los pseudo ::selection y :active también están entre los que más penalizan -de acuerdo a los inspectores-. Lo puedo entender en :active pero no estoy seguro del porqué con ::selection. Quizás un bug de Opera o que es así como trabaja su motor.

  8. Tanto en Opera como en Webkit la propiedad border-radius está entre los que más penalizan y ralentizan el renderizado de la página. Incluso más que las sombras y degradados. Tenga en cuenta que esto no afecta tanto al tiempo de crear el layout (distribución y composición) -como uno podría pensar- sino principalmente al dibujado de la página.

    Como se puede ver en esta página de prueba , he creado un documento con 400 botones. botones Comencé a comprobar cómo los dsitintos estilos afectan el rendimiento de representación ("repaint time" en el analizador). La versión básica del botón sólo tenía estos estilos: botón basebackground: #F6F6F6; border: 1px solid rgba(0, 0, 0, 0.3); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; height: 32px; vertical-align: middle; padding: 7px 10px; float: left; margin: 5px; Luego añadí gradualmente más estilos, registrando el cambio en el tiempo de repintado. La versión final tenía estos estilos adicionales, y demoraba 177ms para pintarla -¡un aumento de 30 veces!-. boton resultante text-shadow: rgba(255, 255, 255, 0.796875) 0px 1px 0px; box-shadow: rgb(255, 255, 255) 0px 1px 1px 0px inset, rgba(0, 0, 0, 0.0976563) 0px 2px 3px 0px; border-radius: 13px; background: -o-linear-gradient(bottom, #E0E0E0 50%, #FAFAFA 100%); opacity: 0.9; color: rgba(0,0,0,0.5); La distribución exacta de cada una de estas propiedades fue como sigue: resultado 1 test El text-shadow y lineal-gradient se encuentran entre los menos penalizadores. Color RGBA (), Opacidad y transparencia fueron un poco más. Luego box-shadow, con un recuadro ( 0 0 1px 1px ) ligeramente más rápido que uno normal ( 0 2px 3px 0 ). Por último, el valor tan inesperadamente alto de border-radius.

    También intenté transform con el parámetro rotate (sólo 1 grado) y dieron cifras realmente elevadas. El desplazar de la página (scroll) -con 400 botones ligeramente girados en ella- es también notablemente brusco (a saltos). Estoy seguro de que no es fácil transformar arbitrariamente un elemento en una página. O ¿tal vez este es un caso de la falta de optimización? Por curiosidad, he comprobado los distintos grados de rotación y obtuve lo siguiente: resultado 1 test Ten en cuenta que incluso un giro de 0,01 grados es muy penalizante. Y a medida que el ángulo aumenta, el rendimiento parece caer, aunque no linealmente sino aparentemente de forma ondulada (con un máximo de 45 grados, y luego cae hasta los 90 grados).

    Hay campo para muchas pruebas aquí -Tengo curiosidad para ver las características de rendimiento de las diversas opciones de transformación (translate, scale, skew, etc) en distintos navegadores.
  9. En Opera, el grado del zoom a la página afecta a la composición del layout. Disminuir zoom aumenta el tiempo de procesamiento. Esto es bastante comprensible, ya que más cosas tienen que ser prestadas en la misma zona. Puede parecer un detalle insignificante, pero a fin de mantener las pruebas consistentes, es importante asegurarse de que el nivel de zoom no estropea los cálculos. Tuve que volver a hacer todas mis pruebas después de descubrir esto, sólo para asegurarme de que no estoy comparando peras con manzanas.

    Hablando del zoom, podría ser interesante probar textos disminuidos y ver cómo afecta al rendimiento general de una aplicación - ¿sería útil?-

  10. En Opera, cambiar el tamaño de la ventana del navegador no afecta el rendimiento en la representación. Parece que los cálculos para reconstruir el layout, dibujar y estilizar no se ven afectados por el tamaño de ventana.

  11. En Chrome, cambiar el tamaño de la ventana del navegador afecta al rendimiento . Tal vez Opera es más inteligente que Chrome, y sólo lo hace en las zonas visibles.

  12. En Opera, refrescar la página afecta negativamente el rendimiento. La progresión es visiblemente lineal. Se puede ver en el gráfico cómo el tiempo aumenta poco a poco haciendo más de 40 recargas de páginas (cada uno de esos rectángulos rojos en la parte inferior corresponden a una recarga de la página seguido de unos segundos de espera). Tiempo de dibujado deviene en casi 3 veces más lenta al final. Parece casi como página escapase. Pecando de precavido, siempre utilicé el promedio de los primeros ~5 resultados para obtener números "frescos".
    Script para el test (recargar página): window.onload = function() { setTimeout(function() { var match = location.href.match(/\?(\d+)$/); var index = match ? parseInt(match[1]) : 0; var numReloads = 10; index++; if (index < numReloads) { location.href = location.href.replace(/\?\d+$/, '') + '?' + index; } }, 5000); };
    No he comprobado si refrescar la página afecta al rendimiento en WebKit/Chrome.

  13. N.T.: en este punto el autor del artículo comenta el uso de SASS para remover el selector universal (punto 4) al usarse clases del tipo .ie7 "algo" utilizadas para discriminar e introducir declaraciones específicas para esa familia.


  14. N.T.: Comienza este punto publicando el script usado para obtener los datos en Webkit y un reconocimiento a uno anterior en el que se basó

    Usando el "Timeline" de Chrome y el script testé el button original de la prueba en Opera y obtuve lo siguiente:
    resultado test Al igual que en Opera, border-radius fue uno de los más costosos de renderizar. Sin embargo, linear-gradient era comparativamente más pesado que el de Opera y box-shadow fue muy superior a text-shadow.

    Una cosa a tener en cuenta sobre el Timeline es que sólo ofrece información sobre el "Layout", mientras que el inspector de Opera la da sobre el "reflow" Y "Layout". No estoy seguro de si los datos relativos a la reaplicaión de las reglas en Opera (reflow) se incluye en el "Layout" de WebKit, o si se descarta. Algo a descubrir en el futuro, con el fin de tener resultados más correctos de la prueba.

  15. Cuando casi había terminado con mis resultados, WebKit ha añadido un inspector de selector similar al de Opera: inspector selectores Yo no fui capaz de hacer muchas pruebas con él, pero noté una cosa interesante. La asignación del selector en WebKit fue ligeramente más rápido que el de Opera. En el mismo documento -esa aplicación de una sola página que estaba trabajando (antes de las optimizaciones)- tomó 1,144ms en el selector correspondiente en Opera, y sólo 18ms en WebKit. Esa es una diferencia de ~65x. O algo está fuera en los cálculos de uno de los motores, o WebKit es realmente mucho más rápido en la concordancia del selector. Por si sirve de algo, el cronograma de Chrome mostraba ~37ms para el recálculo total del estilo (mucho más cerca de WebKit), y ~52ms para volver a dibujarla (en comparación con ~225ms totales de Opera; diferente, pero mucho más cerca). No fui capaz de guardar los datos del "Timeline" en WebKit, por lo que no pude registrar el reflujo y volver a obtener sus números.

Resumen

  • Reducir el número total de selectores (incluyendo los relativos IE: .ie7 .foo .bar)
  • Evitar los selectores universales (incluyendo selectores de atributos indeterminados: [type="url"] )
  • El Zoom de la página afecta el rendimiento CSS en algunos navegadores (por ejemplo, Opera)
  • El tamaño de la ventana afecta al rendimiento CSS en algunos navegadores (por ejemplo, Chrome)
  • Refrescar la página puede afectar negativamente al rendimiento CSS en algunos navegadores (por ejemplo, Opera)
  • "Border-radius" y "transform" se encuentran entre las propiedades más costosas (por lo menos en WebKit y Opera)
  • La pestaña "Timeline" en los navegadores basados ​​en WebKit puede arrojar luz sobre el tiempo total en recálculos/reasignaciones/ redibujados sucesivos
  • La reasignación del Selector es mucho más rápida en WebKit

Preguntas

Al terminar estas notas, tengo un montón de otras cuestiones pendientes relacionadas con el rendimiento CSS:

  • Selectores de atributos entrecomillados frente a los no entrecomillados, por ejemplo [type=search] vs [type="search"], ¿Cómo afecta al rendimiento de la concordancia del selector?
  • ¿Cuáles son las características del rendimiento de múltiples box-shadows/text-shadows/backgrounds? 1 text-shadow frente a 3 ó 5.
  • Rendimiento de los pseudoelementos en selectores (:before, :after).
  • ¿Cómo afectan diferentes valores de border-radius? ¿Penaliza más un valor más alto? ¿Crece linealmente?
  • ¿Declaraciones con !important castigan el rendimiento?
  • ¿La aceleración por hardware influye en el rendimiento?
  • ¿Las diferentes combinaciones de estilos penalizan? (Por ejemplo, text-shadow con gradiente lineal frente a text-shadow con el fondo de un solo color)

Futuro

A medida que nuestras páginas/aplicaciones sean más interactivas la complejidad de los CSS aumentará y los navegadores darán soporte a más y más características "avanzadas" de CSS, el rendimiento del CSS probablemente será aún más importante. Las herramientas existentes están sólo rascando la superficie. Necesitamos otras para pruebas en móviles y herramientas en más navegadores (IE, Firefox). Creé una nota en Mozilla, así que tal vez veamos algo para él pronto. Me encantaría ver los datos de rendimiento de CSS vía script, por lo que podíamos utilizar herramientas como jsperf.com (¿cssperf.com?). Mientras tanto, hay un montón de pruebas que se realizan con analizadores existentes. ¿A qué estás esperando? ;)

Créditos y reconocimiento. Sobre el autor de este artículo

Por @Kangax
Artículo original de Juriy Zaytsev publicado en Perfection Kills el 4 de Enero de 2012.
Este artículo NO se publica bajo la licencia Beerware de este blog sino bajo la que su autor usa.
Todos los créditos y reconocimientos, incluidas las imágenes, a él Los errores de traducción, míos.

Disclaimer del blog

autorización Kangax

Mis disculpas por los fallos o errores tanto en la traducción como interpretación de los conceptos expuestos.
Si encuentras algún pasaje mejorable, déjame tu propuesta en los comentarios. Gracias.
Este artículo se publica tras la autorización (vía twitter) de su autor.