Intercalado de recursos en frameworks de JavaScript

Mejora el procesamiento de imagen con contenido más grande en todo el ecosistema de JavaScript.

Como parte del proyecto Aurora, Google trabajó con frameworks web populares para garantizar que tengan un buen rendimiento según las Métricas web esenciales. Angular y Next.js ya implementaron la incorporación de fuentes, que se explica en la primera parte de este artículo. La segunda optimización que abordaremos es la incorporación de CSS crítica, que ahora está habilitada de forma predeterminada en Angular CLI y tiene una implementación en curso en Nuxt.js.

Inclusión de fuentes

Después de analizar cientos de aplicaciones, el equipo de Aurora descubrió que los desarrolladores suelen incluir fuentes en sus aplicaciones haciendo referencia a ellas en el elemento <head> de index.html. A continuación, se muestra un ejemplo de cómo se vería esto cuando se incluyen los íconos de Material:

<!doctype html>
<html lang="en">
<head>
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  ...
</html>

Si bien este patrón es completamente válido y funcional, bloquea la renderización de la aplicación y agrega una solicitud adicional. Para comprender mejor lo que sucede, observa el código fuente de la hoja de estilo a la que se hace referencia en el código HTML anterior:

/* fallback */
@font-face {
  font-family: 'Material Icons';
  font-style: normal;
  font-weight: 400;
  src: url(https://fonts.gstatic.com/font.woff2) format('woff2');
}

.material-icons {
  /*...*/
}

Observa cómo la definición font-face hace referencia a un archivo externo alojado en fonts.gstatic.com. Cuando se carga la aplicación, el navegador primero debe descargar la hoja de estilo original a la que se hace referencia en el encabezado.

Una imagen que muestra cómo el sitio web tiene que enviar una solicitud al servidor y descargar la hoja de estilo externa
Primero, el sitio web carga la hoja de estilo de fuente.

A continuación, el navegador descarga el archivo woff2 y, por último, puede continuar con la renderización de la aplicación.

Una imagen que muestra las dos solicitudes realizadas, una para la hoja de estilo de la fuente y la segunda para el archivo de fuente.
A continuación, se realiza una solicitud para cargar la fuente.

Una oportunidad de optimización es descargar la hoja de estilo inicial durante el tiempo de compilación y, luego, intercalarla en index.html. De esta forma, se omite un recorrido completo de ida y vuelta a la CDN en el entorno de ejecución, lo que reduce el tiempo de bloqueo.

Cuando se compila la aplicación, se envía una solicitud a la CDN, que recupera la hoja de estilo y la intercala en el archivo HTML, y agrega un <link rel=preconnect> al dominio. Si se aplica esta técnica, obtendríamos el siguiente resultado:

<!doctype html>
<html lang="en">
<head>
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin >
  <style type="text/css">
  @font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/font.woff2) format('woff2');}.material-icons{/*...*/}</style>
  ...
</html>

La incorporación de fuentes ahora está disponible en Next.js y Angular

Cuando los desarrolladores de frameworks implementan la optimización en las herramientas subyacentes, facilitan que las aplicaciones existentes y nuevas la habiliten, lo que lleva la mejora a todo el ecosistema.

Esta mejora está habilitada de forma predeterminada en Next.js v10.2 y Angular v11. Ambas son compatibles con la intercalación de fuentes de Google y Adobe. Angular espera presentar la última en la versión 12.2.

Puedes encontrar la implementación de la anidación de fuentes en Next.js en GitHub y mirar el video en el que se explica esta optimización en el contexto de Angular.

Cómo intercalar CSS crítico

Otra mejora consiste en mejorar las métricas de primer procesamiento de imagen con contenido (FCP) y procesamiento de imagen con contenido más grande (LCP) mediante la incorporación de CSS crítico. El CSS crítico de una página incluye todos los estilos que se usan en su renderización inicial. Para obtener más información sobre el tema, consulta Aplaza el CSS no crítico.

Notamos que muchas aplicaciones cargan diseños de forma síncrona, lo que bloquea la renderización de la aplicación. Una solución rápida es cargar los estilos de forma asíncrona. En lugar de cargar las secuencias de comandos con media="all", establece el valor del atributo media en print y, una vez que se complete la carga, reemplaza el valor del atributo por all:

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

Sin embargo, esta práctica puede provocar parpadeos en el contenido sin diseño.

La página parece parpadear a medida que se cargan los estilos.

En el video anterior, se muestra la renderización de una página, que carga sus estilos de forma asíncrona. El parpadeo se produce porque el navegador primero comienza a descargar los estilos y, luego, renderiza el código HTML. Una vez que el navegador descarga los estilos, activa el evento onload del elemento de vínculo, actualiza el atributo media a all y aplica los estilos al DOM.

Durante el tiempo que transcurre entre la renderización del HTML y la aplicación de los estilos, la página no tiene estilo parcial. Cuando el navegador usa los estilos, se produce un parpadeo, lo que genera una mala experiencia del usuario y genera regresiones en el Cambio de diseño acumulativo (CLS).

La incorporación de CSS crítico, junto con la carga de estilo asíncrona, puede mejorar el comportamiento de carga. La herramienta criaturas encuentra qué estilos se usan en la página. Para ello, observa los selectores de una hoja de estilo y los hace coincidir con el código HTML. Cuando encuentra una coincidencia, considera los diseños correspondientes como parte del CSS crítico y los intercala.

Veamos un ejemplo:

Qué no debes hacer
<head>
   <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'">
</head>
<body>
  <section>
    <button class="primary"></button>
  </section>
</body>
/* styles.css */
section button.primary {
  /* ... */
}
.list {
  /* ... */
}

Ejemplo antes de la incorporación.

En el ejemplo anterior, los critters leerán y analizarán el contenido de styles.css. Luego, hará coincidir los dos selectores con el HTML y descubrirá que usamos section button.primary. Por último, los critters intercalarán los estilos correspondientes en el <head> de la página, lo que generará lo siguiente:

Qué debes hacer
<head>
  <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'">
  <style>
  section button.primary {
    /* ... */
  }
  </style>
</head>
<body>
  <section>
    <button class="primary"></button>
  </section>
</body>

Ejemplo después de la incorporación.

Después de incorporar el CSS crítico en el código HTML, verás que el parpadeo de la página desapareció:

La página se carga después de la intercalación de CSS.

La incorporación crítica de CSS ahora está disponible en Angular y habilitada de forma predeterminada en la versión 12. Si tienes la versión 11, configura la propiedad inlineCritical en true en angular.json para activarla. Para habilitar esta función en Next.js, agrega experimental: { optimizeCss: true } a tu next.config.js.

Conclusiones

En esta publicación, mencionamos algunos aspectos de la colaboración entre Chrome y los frameworks web. Si eres autor de un framework y reconoces algunos de los problemas que abordamos en tu tecnología, esperamos que nuestros hallazgos te inspiren a aplicar optimizaciones de rendimiento similares.

Obtén más información sobre las mejoras. Puedes encontrar una lista completa del trabajo de optimización que realizamos para las métricas web esenciales en la publicación Presentamos Aurora.