Optimiza la carga de recursos

En el módulo anterior, se exploró algunas teorías detrás de la ruta de renderización crítica y cómo los recursos que bloquean la renderización y los que bloquean el analizador pueden retrasar la renderización inicial de una página. Ahora que comprendes parte de la teoría detrás de esto, estás listo para aprender algunas técnicas para optimizar la ruta de renderización crítica.

A medida que se carga una página, se hace referencia a muchos recursos dentro de su código HTML, que le proporcionan su apariencia y diseño mediante CSS, así como su interactividad con JavaScript. En este módulo, se aborda una serie de conceptos importantes relacionados con estos recursos y cómo afectan el tiempo de carga de una página.

Bloqueo de la renderización

Como se explicó en el módulo anterior, CSS es un recurso de bloqueo de la renderización, ya que impide que el navegador renderice cualquier contenido hasta que se construya el Modelo de objetos de CSS (CSSOM). El navegador bloquea la renderización para evitar la aparición de destellos de contenido sin estilo (FOUC), lo que no es deseable desde el punto de vista de la experiencia del usuario.

En el video anterior, hay un breve FOUC en el que puedes ver la página sin ningún estilo. Luego, se aplican todos los diseños una vez que el CSS de la página termina de cargarse desde la red, y la versión sin estilo de la página se reemplaza de inmediato por la versión con estilo.

En términos generales, un FOUC es algo que normalmente no ves, pero es importante comprender el concepto para que sepas por qué el navegador bloquea la renderización de la página hasta que se descargue y se aplique CSS a la página. El bloqueo de la renderización no es necesariamente una acción indeseable, pero te recomendamos que mantengas tu CSS optimizada para minimizar la duración.

Bloqueo del analizador

Un recurso que bloquea el analizador interrumpe el analizador de HTML, como un elemento <script> sin los atributos async o defer. Cuando el analizador encuentra un elemento <script>, el navegador debe evaluar y ejecutar la secuencia de comandos antes de continuar con el análisis del resto del HTML. Esto se debe a que las secuencias de comandos pueden modificar el DOM o acceder a él durante un tiempo mientras se construye.

<!-- This is a parser-blocking script: -->
<script src="/script.js"></script>

Cuando usas archivos JavaScript externos (sin async ni defer), el analizador se bloquea desde que se descubre el archivo hasta que se descarga, analiza y ejecuta. Cuando se usa JavaScript intercalado, el analizador se bloquea de manera similar hasta que se analice y ejecute la secuencia de comandos intercalada.

El escáner de precarga

El análisis de precarga es una optimización del navegador en forma de analizador de HTML secundario que analiza la respuesta HTML sin procesar para buscar y recuperar especulativamente recursos antes de que el analizador de HTML principal los descubra. Por ejemplo, el análisis de precarga permitiría que el navegador comience a descargar un recurso especificado en un elemento <img>, incluso si el analizador de HTML está bloqueado mientras se recuperan y procesan recursos como CSS y JavaScript.

Para aprovechar el análisis de precarga, se deben incluir los recursos críticos en el lenguaje de marcado HTML que envía el servidor. El análisis de precarga no puede detectar los siguientes patrones de carga de recursos:

  • Imágenes que carga CSS con la propiedad background-image. Estas referencias de imagen están en CSS y el escáner de precarga no puede detectarlas.
  • Secuencias de comandos cargadas de forma dinámica en forma de lenguaje de marcado de elementos <script> insertados en el DOM mediante JavaScript o módulos cargados con import() dinámicos
  • HTML renderizado en el cliente mediante JavaScript. Este lenguaje de marcado se encuentra en las cadenas de los recursos de JavaScript y el análisis de precarga no puede detectarlo.
  • Declaraciones @import de CSS.

Estos patrones de carga de recursos son recursos de descubrimiento tardío y, por lo tanto, no se benefician del análisis de precarga. Evítalos siempre que sea posible. Sin embargo, si no es posible evitar esos patrones, tal vez puedas usar una sugerencia de preload para evitar demoras en el descubrimiento de recursos.

CSS

CSS determina la presentación y el diseño de una página. Como se describió anteriormente, CSS es un recurso que bloquea la renderización, por lo que optimizarlo podría tener un impacto considerable en el tiempo general de carga de la página.

Reducción

Reducir el tamaño de los archivos CSS reduce el tamaño de los archivos de los recursos CSS, lo que facilita su descarga. Esto se logra principalmente quitando el contenido de un archivo CSS de origen, como espacios y otros caracteres invisibles, y enviando el resultado a un archivo recién optimizado:

/* Unminified CSS: */

/* Heading 1 */
h1 {
  font-size: 2em;
  color: #000000;
}

/* Heading 2 */
h2 {
  font-size: 1.5em;
  color: #000000;
}
/* Minified CSS: */
h1,h2{color:#000}h1{font-size:2em}h2{font-size:1.5em}

En su forma más básica, la reducción de CSS es una optimización eficaz que podría mejorar el FCP de tu sitio web y, tal vez, incluso el LCP, en algunos casos. Las herramientas como los agrupadores pueden realizar esta optimización automáticamente en compilaciones de producción.

Quita los elementos CSS que no se usen

Antes de procesar cualquier contenido, el navegador debe descargar y analizar todas las hojas de estilo. El tiempo necesario para completar el análisis también incluye los diseños que no se usan en la página actual. Si usas un agrupador que combina todos los recursos CSS en un solo archivo, es probable que tus usuarios descarguen más CSS de la necesaria para renderizar la página actual.

Para descubrir el código CSS que no se usa en la página actual, usa la herramienta de cobertura de las herramientas para desarrolladores de Chrome.

Captura de pantalla de la herramienta de cobertura de las Herramientas para desarrolladores de Chrome. En el panel inferior, se selecciona un archivo CSS que muestra una cantidad considerable de código CSS que el diseño de la página actual no usa.
La herramienta de cobertura de las Herramientas para desarrolladores de Chrome es útil para detectar CSS (y JavaScript) que la página actual no usa. Se puede usar para dividir los archivos CSS en varios recursos a fin de que se carguen en diferentes páginas, en lugar de enviar un paquete de CSS mucho más grande que puede retrasar el procesamiento de la página.

Quitar CSS no utilizado tiene un efecto doble: además de reducir el tiempo de descarga, estás optimizando la construcción del árbol de renderización, ya que el navegador debe procesar menos reglas de CSS.

Evita las declaraciones @import de CSS

Si bien puede parecer conveniente, debes evitar las declaraciones @import en CSS:

/* Don't do this: */
@import url('style.css');

De manera similar al funcionamiento del elemento <link> en HTML, la declaración @import en CSS te permite importar un recurso CSS externo desde una hoja de estilo. La mayor diferencia entre estos dos enfoques es que el elemento HTML <link> es parte de la respuesta HTML y, por lo tanto, se descubre mucho antes que un archivo CSS descargado mediante una declaración @import.

Esto se debe a que, para que se descubra una declaración de @import, primero debes descargar el archivo CSS que la contiene. Esto da como resultado lo que se conoce como una cadena de solicitud que, en el caso de los CSS, retrasa el tiempo que tarda una página en procesarse inicialmente. Otra desventaja es que el analizador de precarga no puede detectar las hojas de estilo que se cargan con una declaración @import y, por lo tanto, se convierten en recursos de bloqueo de renderización tardíos.

<!-- Do this instead: -->
<link rel="stylesheet" href="style.css">

En la mayoría de los casos, puedes reemplazar @import con un elemento <link rel="stylesheet">. Los elementos <link> permiten que las hojas de estilo se descarguen de forma simultánea y reducen el tiempo de carga general, a diferencia de las declaraciones @import, que descargan las hojas de estilo de forma consecutiva.

Intercalado de CSS crítico

El tiempo que tarda descargar archivos CSS puede aumentar el FCP de una página. La intercalación de estilos críticos en el documento <head> elimina la solicitud de red de un recurso de CSS y, cuando se hace correctamente, puede mejorar los tiempos de carga iniciales cuando la caché del navegador de un usuario no está preparada. El CSS restante se puede cargar de forma asíncrona o agregarse al final del elemento <body>.

<head>
  <title>Page Title</title>
  <!-- ... -->
  <style>h1,h2{color:#000}h1{font-size:2em}h2{font-size:1.5em}</style>
</head>
<body>
  <!-- Other page markup... -->
  <link rel="stylesheet" href="non-critical.css">
</body>

La desventaja es que integrar una gran cantidad de CSS agrega más bytes a la respuesta HTML inicial. Debido a que los recursos HTML a menudo no se pueden almacenar en caché por mucho tiempo (o por mucho tiempo), esto significa que el CSS intercalado no se almacena en caché para las páginas posteriores que podrían usar el mismo CSS en hojas de estilo externas. Prueba y mide el rendimiento de tu página para asegurarte de que las compensaciones valen la pena.

Demostraciones de CSS

JavaScript

JavaScript genera la mayor parte de la interactividad en la Web, pero tiene un costo. Enviar demasiado código JavaScript puede hacer que tu página web tarde en responder durante la carga de la página e, incluso, causar problemas de capacidad de respuesta que ralentizan las interacciones, lo que puede ser frustrante para los usuarios.

JavaScript que bloquea el procesamiento

Cuando se cargan elementos <script> sin los atributos defer o async, el navegador bloquea el análisis y el procesamiento hasta que se descarga, analiza y ejecuta la secuencia de comandos. Del mismo modo, las secuencias de comandos intercaladas bloquean el analizador hasta que se analiza y ejecuta la secuencia de comandos.

async en comparación con defer

async y defer permiten que las secuencias de comandos externas se carguen sin bloquear el analizador HTML, mientras que las secuencias de comandos (incluidas las intercaladas) con type="module" se aplazan automáticamente. Sin embargo, async y defer tienen algunas diferencias que es importante comprender.

Representación de varios mecanismos de carga de secuencias de comandos, en los que se detallan las funciones de analizador, recuperación y ejecución en función de diversos atributos usados, como asíncrono, defer, type=&#39;module&#39; y una combinación de los tres.
Fuente de https://html.spec.whatwg.org/multipage/scripting.html

Las secuencias de comandos cargadas con async se analizan y se ejecutan de inmediato una vez descargadas, mientras que las que se cargan con defer se ejecutan cuando finaliza el análisis del documento HTML. Esto ocurre al mismo tiempo que el evento DOMContentLoaded del navegador. Además, las secuencias de comandos async pueden ejecutarse desordenadamente, mientras que las secuencias de comandos defer se ejecutan en el orden en que aparecen en el lenguaje de marcado.

Renderización del cliente

En general, debes evitar el uso de JavaScript para procesar el contenido crítico o el elemento de LCP de una página. Esto se conoce como renderización del lado del cliente y es una técnica muy utilizada en aplicaciones de una sola página (SPA).

El lenguaje de marcado renderizado por JavaScript evita el análisis de precarga, ya que los recursos contenidos en el lenguaje de marcado renderizado por el cliente no son detectables. Esto podría retrasar la descarga de recursos cruciales, como una imagen LCP. El navegador solo comienza a descargar la imagen LCP después de que se ejecuta la secuencia de comandos y se agrega el elemento al DOM. A su vez, la secuencia de comandos solo se puede ejecutar después de que se descubre, se descarga y se analiza. Esto se conoce como una cadena de solicitudes crítica y debe evitarse.

Además, el lenguaje de marcado que se usa con JavaScript tiene más probabilidades de generar tareas largas que el lenguaje de marcado descargado del servidor en respuesta a una solicitud de navegación. El uso extensivo del procesamiento de HTML del cliente puede afectar negativamente la latencia de interacción. Esto ocurre especialmente en los casos en los que el DOM de una página es muy grande, lo que activa un trabajo de renderización significativo cuando JavaScript modifica el DOM.

Reducción

Al igual que con CSS, la reducción de JavaScript reduce el tamaño de archivo del recurso de secuencia de comandos. De esta manera, se pueden realizar descargas más rápidas, lo que permite que el navegador pase al proceso de análisis y compilación de JavaScript más rápidamente.

Además, la reducción de JavaScript va un paso más allá de reducir otros elementos, como CSS. Cuando se reduce JavaScript, no solo se quitan los espacios, las pestañas y los comentarios, sino que los símbolos de JavaScript de origen se acortan. Este proceso a veces se conoce como uglificación. Para ver la diferencia, toma el siguiente código fuente de JavaScript:

// Unuglified JavaScript source code:
export function injectScript () {
  const scriptElement = document.createElement('script');
  scriptElement.src = '/js/scripts.js';
  scriptElement.type = 'module';

  document.body.appendChild(scriptElement);
}

Cuando se uglifica el código fuente de JavaScript anterior, el resultado puede verse como el siguiente fragmento de código:

// Uglified JavaScript production code:
export function injectScript(){const t=document.createElement("script");t.src="/js/scripts.js",t.type="module",document.body.appendChild(t)}

En el fragmento anterior, puedes ver que la variable legible por humanos scriptElement en la fuente se acorta a t. Cuando se aplica a una gran colección de secuencias de comandos, el ahorro puede ser bastante significativo, sin afectar las funciones que proporciona el JavaScript de producción de un sitio web.

Si usas un agrupador para procesar el código fuente de tu sitio web, la uglificación suele realizarse automáticamente para las compilaciones de producción. Los errores de configuración, como Terser, también son altamente configurables, lo que te permite ajustar la agresividad del algoritmo de uglificación para lograr el máximo ahorro. Sin embargo, los valores predeterminados de cualquier herramienta de uglificación suelen ser suficientes para lograr el equilibrio adecuado entre el tamaño de salida y la preservación de las capacidades.

Demostraciones de JavaScript

Pon a prueba tus conocimientos

¿Cuál es la mejor manera de cargar varios archivos CSS en el navegador?

La declaración @import de CSS.
Vuelve a intentarlo.
Varios elementos <link>.
Correcto.

¿Qué hace el escáner de precarga del navegador?

Es un analizador de HTML secundario que examina el lenguaje de marcado sin procesar para descubrir recursos antes de que el analizador del DOM pueda encontrarlos antes.
Correcto.
Detecta elementos <link rel="preload"> en un recurso HTML.
Vuelve a intentarlo.

¿Por qué el navegador bloquea temporalmente el análisis de HTML de forma predeterminada cuando se descargan recursos de JavaScript?

Para evitar que aparezca contenido sin estilo (FOUC)
Vuelve a intentarlo.
Porque evaluar JavaScript es una tarea que consume mucha CPU, y pausar el análisis de HTML le brinda más ancho de banda a la CPU para terminar de cargar las secuencias de comandos.
Vuelve a intentarlo.
Porque las secuencias de comandos pueden modificar el DOM o acceder a él de otro modo
Correcto.

A continuación: Ayuda al navegador con sugerencias de recursos

Ahora que tienes un control sobre la forma en que los recursos cargados en el elemento <head> pueden afectar la carga inicial de la página y varias métricas, es hora de avanzar. En el siguiente módulo, se exploran las sugerencias de recursos y se explica cómo pueden brindar sugerencias valiosas al navegador para comenzar a cargar recursos y abrir conexiones a servidores de origen cruzado antes que el navegador sin ellos.