Refactorización de CSS: optimización del tamaño y el rendimiento (parte 3)

Publicado: 2022-03-10
Resumen rápido ↬ La base de código refactorizada debería dar como resultado un rendimiento similar o mejorado y una mejor salud de la base de código. Después de todo, si la implementación del código base refactorizado causa problemas de carga o rendimiento, generará menos tráfico e ingresos. Afortunadamente, hay muchas técnicas de optimización que podemos aplicar para abordar posibles problemas de rendimiento y tamaño de archivo.

En artículos anteriores de esta serie, hemos cubierto la auditoría del estado de la base de código CSS y la estrategia, prueba y mantenimiento de refactorización incremental de CSS. Independientemente de cuánto se haya mejorado la base de código CSS durante el proceso de refactorización y cuánto más mantenible y extensible sea, la hoja de estilo final debe optimizarse para obtener el mejor rendimiento posible y el menor tamaño de archivo posible.

La implementación del código base refactorizado no debería resultar en un peor rendimiento del sitio web ni en una peor experiencia del usuario. Después de todo, los usuarios no esperarán eternamente a que se cargue el sitio web. Además, la administración no estará satisfecha con la disminución del tráfico y los ingresos causados ​​por el código base no optimizado, a pesar de las mejoras en la calidad del código.

En este artículo, cubriremos las estrategias de optimización de CSS que pueden optimizar el tamaño del archivo CSS, los tiempos de carga y el rendimiento de procesamiento. De esa manera, la base de código CSS refactorizada no solo es más fácil de mantener y extensible, sino que también tiene un mejor rendimiento y marca todas las casillas que son importantes tanto para el usuario final como para la administración.

Parte de: Refactorización de CSS

  • Parte 1: Refactorización de CSS: Introducción
  • Parte 2: estrategia de CSS, pruebas de regresión y mantenimiento
  • Parte 3: Optimización del tamaño y el rendimiento
  • Suscríbete a nuestro boletín electrónico para no perderte los próximos.

Optimización del tamaño del archivo de hoja de estilo

La optimización del tamaño del archivo se reduce a eliminar caracteres innecesarios y formatear y optimizar el código CSS para usar diferentes propiedades de sintaxis o taquigrafía para reducir la cantidad total de caracteres en un archivo.

Optimización y Minificación

La optimización y minificación de CSS ha existido durante años y se convirtió en un elemento básico en la optimización de frontend. Herramientas como cssnano y clean-css se encuentran entre mis herramientas favoritas cuando se trata de optimización y minificación de CSS. Ofrecen una amplia variedad de opciones de personalización para controlar aún más cómo se optimiza el código y qué navegadores son compatibles.

Estas herramientas funcionan de manera similar. Primero, el código no optimizado se analiza y transpila siguiendo las reglas establecidas en la configuración. El resultado es el código que usa menos caracteres pero aún conserva el formato (saltos de línea y espacios en blanco).

 /* Before - original and unoptimized code */ .container { padding: 24px 16px 24px 16px; background: #222222; } /* After - optimized code with formatting */ .container { padding: 24px 16px; background: #222; }

Y finalmente, el código optimizado transpilado se minimiza eliminando todo el formato de texto innecesario . Según el código base y los navegadores admitidos establecidos en la configuración, también se puede eliminar el código con prefijos de proveedores obsoletos.

 /* Before - optimized code with formatting */ .container { padding: 24px 16px; background: #222; } /* After - optimized and minified code */ .container{padding:24px 16px;background:#222}

Incluso en este ejemplo básico, logramos reducir el tamaño total del archivo de 76 bytes a 55 bytes, lo que resultó en una reducción del 23 %. Según el código base y las herramientas de optimización y la configuración, la optimización y minificación de CSS pueden ser aún más efectivas.

La optimización y minimización de CSS se puede considerar como una victoria fácil debido a la importante recompensa con solo unos pocos ajustes en el flujo de trabajo de CSS. Es por eso que la minificación debe tratarse como la optimización de rendimiento mínima y un requisito para todas las hojas de estilo del proyecto.

¡Más después del salto! Continúe leyendo a continuación ↓

Optimización de consultas de medios

Cuando escribimos consultas de medios en CSS, especialmente cuando usamos varios archivos (PostCSS o Sass), generalmente no anidamos el código en una sola consulta de medios para un proyecto completo. Para mejorar la capacidad de mantenimiento, la modularidad y la estructura del código, generalmente escribimos las mismas expresiones de consulta de medios para múltiples componentes de CSS.

Consideremos el siguiente ejemplo de una base de código CSS no optimizada.

 .page { display: grid; grid-gap: 16px; } @media (min-width: 768px) { .page { grid-template-columns: 268px auto; grid-gap: 24px; } } /* ... */ .products-grid { display: grid; grid-template-columns: repeat(2, 1fr); grid-gap: 16px; } @media (min-width: 768px) { .products-grid { grid-template-columns: repeat(3, 1fr); grid-gap: 20px; } }

Como puede ver, tenemos un @media (min-width: 768px) por componente para mejorar la legibilidad y el mantenimiento. Ejecutemos la optimización y la minificación en este ejemplo de código y veamos qué obtenemos.

 .page{display:grid;grid-gap:16px}@media (min-width: 768px){.page{grid-template-columns:268px auto;grid-gap:24px}}.products-grid{display:grid;grid-template-columns:repeat(2,1fr);grid-gap:16px}@media (min-width: 768px){.products-grid{grid-template-columns:repeat(3,1fr);grid-gap:20px}}

Esto puede ser un poco difícil de leer, pero todo lo que tenemos que notar es la consulta de medios @media (min-width: 768px) . Ya llegamos a la conclusión de que queremos reducir la cantidad de caracteres en una hoja de estilo y podemos anidar varios selectores en una sola consulta de medios, entonces, ¿por qué el minificador no eliminó la expresión duplicada? Hay una razón simple para eso.

El orden de las reglas es importante en CSS, por lo que para fusionar las consultas de medios duplicadas, es necesario mover los bloques de código. Esto dará como resultado que se cambie el orden de las reglas, lo que puede causar efectos secundarios no deseados en los estilos.

Sin embargo, la combinación de consultas de medios podría hacer que el tamaño del archivo sea aún más pequeño, según el código base y la estructura. Herramientas y paquetes como postcss-sort-media-queries nos permiten eliminar consultas de medios duplicadas y reducir aún más el tamaño del archivo.

Por supuesto, existe la importante advertencia de tener una estructura base de código CSS bien estructurada que no dependa del orden de las reglas. Esta optimización debe tenerse en cuenta al planificar la refactorización de CSS y establecer reglas básicas.

Recomendaría verificar primero si el beneficio de la optimización supera los riesgos potenciales. Esto se puede hacer fácilmente ejecutando una auditoría de CSS y verificando las estadísticas de consulta de medios. Si es así, recomendaría agregarlo más adelante y ejecutar pruebas de regresión automatizadas para detectar cualquier efecto secundario inesperado y errores que puedan ocurrir como resultado.

Eliminar CSS no utilizado

Durante el proceso de refactorización, siempre existe la posibilidad de que termine con algunos estilos heredados sin usar que no se hayan eliminado por completo o que tenga algunos estilos recién agregados que no se están usando. Estos estilos también aumentan el número total de caracteres y el tamaño del archivo. Sin embargo, eliminar estos estilos no utilizados mediante herramientas automatizadas puede ser algo arriesgado porque las herramientas no pueden predecir con precisión qué estilos se utilizan realmente.

Herramientas como purgecss revisan todos los archivos en el proyecto y usan todas las clases mencionadas en los archivos como selectores, solo para pecar de precavidos y no eliminar accidentalmente los selectores para elementos dinámicos inyectados con JavaScript, entre otros casos potenciales. Sin embargo, purgecss ofrece opciones de configuración flexibles como soluciones para estos problemas y riesgos potenciales.

Sin embargo, esta mejora debe realizarse solo cuando los beneficios potenciales superen los riesgos . Además, esta técnica de optimización requerirá un tiempo considerable para instalarse, configurarse y probarse, y podría causar problemas no deseados en el futuro, así que proceda con precaución y asegúrese de que la configuración sea infalible.

Eliminar el CSS que bloquea el renderizado

De forma predeterminada, CSS es un recurso que bloquea la representación, lo que significa que el sitio web no se mostrará al usuario hasta que el navegador haya descargado y analizado todas las hojas de estilo vinculadas y sus dependencias (fuentes, por ejemplo).

Ejemplo de CSS de bloqueo de procesamiento con hoja de estilo de fuente y dependencia de archivo de fuente
Ejemplo de CSS de bloqueo de procesamiento con hoja de estilo de fuente y dependencia de archivo de fuente. (De web.dev bajo licencia Creative Commons Attribution 4.0) (Vista previa grande)

Si el archivo de hoja de estilo tiene un tamaño de archivo grande o varias dependencias que se encuentran en servidores o CDN de terceros, la representación del sitio web puede retrasarse significativamente según la velocidad y la confiabilidad de la red.

La pintura con contenido más grande (LCP) se ha convertido en una métrica importante en los últimos meses. LCP no solo es importante para el rendimiento, sino también para el SEO: los sitios web con mejores puntajes de LCP tendrán una mejor clasificación en los resultados de búsqueda. Eliminar los recursos que bloquean el renderizado, como CSS, es una forma de mejorar la puntuación de LCP.

Sin embargo, si pospusiéramos la carga y el procesamiento de la hoja de estilo, esto daría como resultado un Flash de contenido sin estilo (FOUC): el contenido se mostraría al usuario de inmediato y los estilos se cargarían y aplicarían unos momentos después. Este cambio puede parecer discordante e incluso puede confundir a algunos usuarios.

CSS crítico

Con Critical CSS, podemos asegurarnos de que el sitio web se cargue con la cantidad mínima de estilos que se garantiza que se usarán en la página cuando se represente inicialmente. De esta forma, podemos hacer que el FOUC sea mucho menos notorio o incluso eliminarlo en la mayoría de los casos. Por ejemplo, si la página de inicio presenta un componente de encabezado con navegación y un componente principal ubicado en la parte superior de la página, esto significa que el CSS crítico contendrá todos los estilos globales y de componentes necesarios para estos componentes, mientras que los estilos para otros componentes en la página será diferido.

Este CSS está integrado en HTML bajo una etiqueta de style , por lo que los estilos se cargan y analizan junto con el archivo HTML. Aunque esto dará como resultado un tamaño de archivo HTML ligeramente más grande (que también debe minimizarse), todos los demás CSS no críticos se pospondrán y no se cargarán de inmediato y el sitio web se procesará más rápido. Con todo, los beneficios superan el aumento en el tamaño del archivo HTML.

 <head> <style type="text/css"><!-- Minified Critical CSS markup --></style> </head>

Existen muchas herramientas automatizadas y paquetes NPM, según su configuración, que pueden extraer CSS crítico y generar hojas de estilo diferidas.

Aplazamiento de hojas de estilo

¿Cómo hacemos exactamente que el CSS no bloquee? Sabemos que no se debe hacer referencia a él en el elemento de head HTML cuando se descarga por primera vez el HTML de la página. Demian Renzulli ha esbozado este método en su artículo.

No existe un enfoque de HTML nativo (hasta el momento) para optimizar o diferir la carga de recursos que bloquean el procesamiento, por lo que debemos usar JavaScript para insertar la hoja de estilo no crítica en el marcado HTML después del procesamiento inicial. También debemos asegurarnos de que estos estilos se carguen de forma no óptima (bloqueo de procesamiento) si un usuario visita la página con JavaScript no habilitado en el navegador.

 <!-- Deferred stylesheet --> <link rel="preload" as="style" href="path/to/stylesheet.css" onload="this.onload=null;this.rel='stylesheet'"> <!-- Fallback --> <noscript> <link rel="stylesheet" href="path/to/stylesheet.css"> </noscript>

Con el link rel="preload" as="style" se asegura de que el archivo de hoja de estilo se solicite de forma asincrónica, mientras que el controlador de JavaScript onload se asegura de que el navegador cargue y procese el archivo después de que el documento HTML haya terminado de cargarse. Se necesita algo de limpieza, por lo que debemos establecer onload en null para evitar que esta función se ejecute varias veces y provoque re-procesamientos innecesarios.

Así es exactamente como Smashing Magazine maneja sus hojas de estilo. Cada plantilla (página de inicio, categorías de artículos, páginas de artículos, etc.) tiene un CSS crítico específico de la plantilla insertado dentro de la etiqueta de style HTML en el elemento head y una hoja de estilo main.css diferida que contiene todos los estilos no críticos.

Sin embargo, en lugar de alternar el parámetro rel , aquí podemos ver que la consulta de medios cambia de los medios de print de baja prioridad diferidos automáticamente al atributo de prioridad alta all cuando la página ha terminado de cargarse. Este es un enfoque alternativo igualmente viable para diferir la carga de hojas de estilo no críticas.

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

División y carga condicional de hojas de estilo con consultas de medios

Para los casos en los que el archivo final de la hoja de estilo tenga un tamaño de archivo grande incluso después de que se hayan aplicado las optimizaciones antes mencionadas, puede dividir las hojas de estilo en varios archivos en función de las consultas de medios y usar la propiedad de medios en las hojas de estilo a las que se hace referencia en el elemento HTML del enlace para cargarlas condicionalmente. .

 <link href="print.css" rel="stylesheet" media="print"> <link href="mobile.css" rel="stylesheet" media="all"> <link href="tablet.css" rel="stylesheet" media="screen and (min-width: 768px)"> <link href="desktop.css" rel="stylesheet" media="screen and (min-width: 1366px)">

De esa forma, si se utiliza un enfoque de dispositivos móviles primero, los estilos para pantallas de mayor tamaño no se descargarán ni analizarán en dispositivos móviles que podrían estar funcionando en redes más lentas o poco confiables.

Solo para reiterar, este método debe usarse si el resultado de los métodos de optimización mencionados anteriormente da como resultado una hoja de estilo con un tamaño de archivo subóptimo. Para casos regulares, este método de optimización no será tan efectivo o impactante, dependiendo del tamaño de la hoja de estilo individual.

Aplazamiento de archivos de fuentes y hojas de estilo

Aplazar las hojas de estilo de fuentes (archivos de fuentes de Google, por ejemplo) también podría ser beneficioso para el rendimiento de renderizado inicial. Hemos llegado a la conclusión de que las hojas de estilo bloquean la visualización, pero también lo hacen los archivos de fuentes a los que se hace referencia en la hoja de estilo. Los archivos de fuentes también agregan un poco de sobrecarga al rendimiento de procesamiento inicial.

Cargar hojas de estilo de fuentes y archivos de fuentes es un tema complejo y profundizar en él requeriría un artículo completamente nuevo solo para explicar todos los enfoques viables. Afortunadamente, Zach Leatherman ha esbozado muchas estrategias viables en esta increíble guía completa y ha resumido los pros y los contras de cada enfoque. Si usa Google Fonts, Harry Roberts ha esbozado una estrategia para la carga más rápida de Google Fonts.

Si decide diferir las hojas de estilo de fuente, terminará con Flash of Unstyled Text (FOUT). La página se representará inicialmente con la fuente alternativa hasta que se hayan descargado y analizado los archivos de fuente diferidos y las hojas de estilo, momento en el que se aplicarán los nuevos estilos. Este cambio puede ser muy notorio y puede causar cambios de diseño y confundir a los usuarios, según el caso individual.

Barry Pollard ha esbozado algunas estrategias que pueden ayudarnos a lidiar con FOUT y habló sobre la próxima función CSS de ajuste de tamaño que proporcionará una forma más sencilla y nativa de lidiar con FOUT.

Optimizaciones del lado del servidor

Compresión HTTP

Además de la minimización y la optimización del tamaño del archivo, los activos estáticos como HTML, archivos CSS, archivos JavaScript, etc. Los algoritmos de compresión HTTP como Gzip y Brotli se pueden usar para reducir aún más el tamaño del archivo descargado.

La compresión HTTP debe configurarse en el servidor, lo que depende de la pila tecnológica y la configuración. Sin embargo, los beneficios de rendimiento pueden variar y pueden no tener tanto impacto como la minimización y optimización de hojas de estilo estándar, ya que los navegadores seguirán descomprimiendo los archivos comprimidos y tendrán que analizarlos.

Almacenamiento en caché de hojas de estilo

El almacenamiento en caché de archivos estáticos es una estrategia de optimización útil. Los navegadores aún tendrán que descargar los archivos estáticos del servidor en la primera carga, pero una vez que se almacenen en caché, se cargarán directamente en las solicitudes posteriores, acelerando el proceso de carga.

El almacenamiento en caché se puede controlar a través del encabezado HTTP Cache-Control en el nivel del servidor (por ejemplo, usando el archivo .htaccess en un servidor Apache).

Con max-age podemos indicar cuánto tiempo debe permanecer el archivo en caché (en segundos) en el navegador y con public , estamos indicando que el navegador y cualquier otro caché pueden almacenar en caché el archivo.

 Cache-Control: public, max-age=604800

Se puede lograr una estrategia de caché más agresiva y efectiva para activos estáticos con una configuración immutable . Esto le dice al navegador que este archivo en particular nunca cambiará y que cualquier nueva actualización hará que este archivo se elimine y un nuevo archivo con un nombre de archivo diferente ocupará su lugar. Esto se conoce como cache-busting .

 Cache-Control: public, max-age=604800, immutable

Sin una estrategia adecuada de eliminación de caché , existe el riesgo de perder el control sobre los archivos que se almacenan en caché en el navegador del usuario. Lo que significa que si el archivo cambiara, el navegador no podrá saber que debe descargar el archivo actualizado y no usar el archivo en caché desactualizado. Y a partir de ese momento, prácticamente no hay nada que podamos hacer para solucionarlo y el usuario se quedará con el archivo desactualizado hasta que caduque.

Para las hojas de estilo, eso podría significar que si tuviéramos que actualizar los archivos HTML con contenido nuevo y componentes que requieren un estilo nuevo, estos estilos no se mostrarán porque la hoja de estilo desactualizada se almacena en caché sin una estrategia de prevención de caché y el navegador no lo sabrá. tiene que descargar el nuevo archivo.

Antes de utilizar una estrategia de almacenamiento en caché para hojas de estilo o cualquier otro archivo estático, se deben implementar mecanismos efectivos de prevención de caché para evitar que los archivos estáticos obsoletos se atasquen en la memoria caché del usuario. Puede utilizar uno de los siguientes mecanismos de control de versiones para la prevención de caché:

  • Agregar una cadena de consulta al nombre del archivo.
    Por ejemplo styles.css?v=1.0.1. Sin embargo, algunos CDN pueden ignorar por completo o eliminar la cadena de consulta del nombre del archivo y hacer que el archivo se atasque en la memoria caché del usuario y nunca se actualice.
  • Cambiar el nombre del archivo o agregar un hash.
    Por ejemplo styles.a1bc2.css o styles.v1.0.1.css. Esto es más confiable y efectivo que agregar una cadena de consulta al nombre del archivo.

¿CDN o alojamiento propio?

Content Delivery Network (CDN) es un grupo de servidores distribuidos geográficamente que se usan comúnmente para la entrega confiable y rápida de activos estáticos como imágenes, videos, archivos HTML, archivos CSS, archivos JavaScript, etc.

Aunque las CDN pueden parecer una gran alternativa a los activos estáticos de alojamiento propio, Harry Roberts ha realizado una investigación exhaustiva sobre el tema y concluyó que los activos de alojamiento propio son más beneficiosos para el rendimiento.

“Realmente hay muy pocas razones para dejar sus activos estáticos en la infraestructura de otra persona. Los beneficios percibidos son a menudo un mito, e incluso si no lo fueran, las compensaciones simplemente no valen la pena. La carga de activos de múltiples orígenes es demostrablemente más lenta”.

Dicho esto, recomendaría el alojamiento propio de las hojas de estilo (hojas de estilo de fuentes incluidas, si es posible) de forma predeterminada y cambiar a CDN solo si existen razones viables u otros beneficios para hacerlo.

Auditoría del tamaño y rendimiento del archivo CSS

WebPageTest y otras herramientas de auditoría de rendimiento similares se pueden usar para obtener una descripción detallada del proceso de carga del sitio web, los tamaños de archivo, los recursos de bloqueo de procesamiento, etc. Estas herramientas pueden brindarle una idea de cómo se carga su sitio web en una amplia gama de dispositivos : desde una PC de escritorio que se ejecuta en una red de alta velocidad hasta teléfonos inteligentes de gama baja que se ejecutan en redes lentas y poco confiables.

Hagamos una auditoría de rendimiento en un sitio web mencionado en el primer artículo de esta serie, el que tiene 2 MB de CSS minificado.

Primero, echaremos un vistazo al desglose del contenido para determinar qué recursos ocupan más ancho de banda. En los siguientes gráficos, podemos ver que las imágenes ocupan la mayoría de las solicitudes, lo que significa que deben cargarse de forma diferida. En el segundo gráfico, podemos ver que las hojas de estilo y los archivos JavaScript son los más grandes en términos de tamaño de archivo. Esta es una buena indicación de que estos archivos deben minimizarse y optimizarse, refactorizarse o dividirse en varios archivos y cargarse de forma asíncrona.

Dos gráficos que muestran el desglose del contenido por tipo MIME
Desglose de contenido por tipo MIME (en la primera vista). (Vista previa grande)

Podemos sacar aún más conclusiones de los gráficos Web Vitals. Echando un vistazo al gráfico de pintura con contenido más grande (LCP), podemos obtener una descripción detallada de los recursos que bloquean el renderizado y cuánto afectan al renderizado inicial.

Ya podríamos concluir que la hoja de estilo del sitio web tendrá el mayor impacto en el LCP y las estadísticas de carga. Sin embargo, podemos ver hojas de estilo de fuentes, archivos JavaScript e imágenes a las que se hace referencia dentro de las hojas de estilo que también bloquean el renderizado. Sabiendo que podemos aplicar los métodos de optimización antes mencionados para reducir el tiempo de LCP al eliminar los recursos que bloquean el renderizado.

el gráfico de pintura con contenido más grande
Un gráfico para la pintura con contenido más grande que ocurre en 8561 ms. Observe la bombilla naranja en la línea de tiempo en la lista de recursos: estos recursos están bloqueando el procesamiento. (Vista previa grande)

Conclusión

El proceso de refactorización no está completo cuando se ha mejorado el estado y la calidad del código y cuando se han solucionado las debilidades y los problemas de la base de código. El código base refactorizado debería dar como resultado un rendimiento igual o mejorado en comparación con el código base heredado.

Los usuarios finales no deberían experimentar problemas de rendimiento o largos tiempos de carga desde el código base refactorizado. Afortunadamente, existen muchos métodos para asegurarse de que las bases de código sean sólidas y eficaces, desde los métodos simples de minimización y optimización hasta los métodos más complejos, como la eliminación de los recursos de bloqueo de procesamiento y la división de código.

Podemos usar varias herramientas de auditoría de rendimiento como WebPageTest para obtener una descripción detallada de los tiempos de carga, el rendimiento, los recursos de bloqueo de procesamiento y otros factores para que podamos abordar estos problemas de manera temprana y efectiva.

Parte de: Refactorización de CSS

  • Parte 1: Refactorización de CSS: Introducción
  • Parte 2: Refactorización de CSS: estrategia, pruebas de regresión y mantenimiento
  • Parte 3: Refactorización de CSS: optimización del tamaño y el rendimiento
  • Suscríbete a nuestro boletín electrónico para no perderte los próximos.

Referencias

  • "CSS de bloqueo de procesamiento", Ilya Grigorik
  • “Aplazar el CSS no crítico”, Demian Renzulli
  • "Una guía completa para las estrategias de carga de fuentes", Zach Leatherman
  • “Una nueva forma de reducir el impacto de la carga de fuentes: descriptores de fuentes CSS”, Barry Pollard
  • "Autohospede sus activos estáticos", Harry Roberts
  • "Optimizar la carga y el renderizado de WebFont", Ilya Grigorik