Optimiza JavaScript de terceros

Las secuencias de comandos de terceros afectan el rendimiento, por lo que es importante auditarlas con regularidad y usar técnicas eficientes para cargarlas. En este codelab, se muestra cómo optimizar la carga de recursos de terceros. Abarca las siguientes técnicas:

  • Cómo posponer la carga de la secuencia de comandos

  • Carga diferida de recursos no críticos

  • Establece una conexión previa con los orígenes necesarios

La app de ejemplo incluida presenta una página web simple con tres funciones provenientes de fuentes externas:

  • Incorporación de video

  • Biblioteca de visualización de datos para renderizar un gráfico de líneas

  • Un widget para compartir contenido en redes sociales

Captura de pantalla de la página con recursos de terceros destacados.
Recursos de terceros en la app de ejemplo.

Comenzarás por medir el rendimiento de la aplicación y, luego, aplicarás cada técnica para mejorar diferentes aspectos de su rendimiento.

Cómo medir el rendimiento

Primero, abre la app de ejemplo en la vista de pantalla completa:

  1. Haz clic en Remix to Edit para que el proyecto sea editable.
  2. Para obtener una vista previa del sitio, presiona Ver app. Luego, presiona Pantalla completa pantalla completa.

Ejecuta una auditoría de rendimiento de Lighthouse en la página para establecer el rendimiento del modelo de referencia:

  1. Presiona "Control + Mayús + J" (o bien "Comando + Opción + J" en Mac) para abrir Herramientas para desarrolladores.
  2. Haz clic en la pestaña Lighthouse.
  3. Haz clic en Dispositivo móvil.
  4. Selecciona la casilla de verificación Rendimiento. (Puedes desmarcar el resto de las casillas de verificación en la sección Auditorías).
  5. Haz clic en Simulación rápida de 3G, 4x CPU Slowdown.
  6. Selecciona la casilla de verificación Liberar espacio de almacenamiento.
  7. Haz clic en Ejecutar auditorías.

Cuando ejecutas una auditoría en tu máquina, los resultados exactos pueden variar, pero deberías notar que el tiempo de First Contentful Paint (FCP) es bastante alto y Lighthouse sugiere dos oportunidades para investigar: eliminar los recursos que bloquean la renderización y Preconectarte a los orígenes necesarios. (Incluso si todas las métricas están en verde, las optimizaciones seguirán generando mejoras).

Captura de pantalla de la auditoría de Lighthouse en la que se muestra un FCP de 2.4 segundos y dos oportunidades: eliminar los recursos de bloqueo de renderización y la preconexión a los orígenes necesarios.

Aplaza JavaScript de terceros

La auditoría Eliminación de recursos de bloqueo de renderización identificó que puedes ahorrar tiempo si aplazas una secuencia de comandos que proviene de d3js.org:

Captura de pantalla de la auditoría de eliminación de recursos de bloqueo de renderización con la secuencia de comandos d3.v3.min.js destacada.

D3.js es una biblioteca de JavaScript para crear visualizaciones de datos. El archivo script.js de la app de ejemplo usa funciones de utilidad de D3 para crear el gráfico de líneas SVG y agregarlo a la página. El orden de las operaciones es importante aquí: script.js se debe ejecutar después de que se analiza el documento y se carga la biblioteca D3, por lo que se incluye justo antes de la etiqueta de cierre </body> en index.html.

Sin embargo, la secuencia de comandos D3 se incluye en el <head> de la página, lo que bloquea el análisis del resto del documento:

Captura de pantalla de index.html con la etiqueta de script destacada en el encabezado.

Hay dos atributos mágicos que pueden desbloquear el analizador cuando se los agrega a la etiqueta de la secuencia de comandos:

  • async garantiza que las secuencias de comandos se descarguen en segundo plano y se ejecuten en la primera oportunidad después de que terminen de descargarse.

  • defer garantiza que las secuencias de comandos se descarguen en segundo plano y se ejecuten después de que el análisis finalice por completo.

Dado que este gráfico no es realmente fundamental para la página general y es probable que esté en la mitad inferior de la página, usa defer para asegurarte de que no haya bloqueo del analizador.

Paso 1: Carga la secuencia de comandos de forma asíncrona con el atributo defer.

En la línea 17 de index.html, agrega el atributo defer al elemento <script>:

<script src="https://d3js.org/d3.v3.min.js" defer></script>

Paso 2: Asegúrate de mantener el orden correcto de las operaciones

Ahora que D3 se aplaza, script.js se ejecutará antes de que D3 esté listo, lo que generará un error.

Las secuencias de comandos con el atributo defer se ejecutan en el orden en que se especificaron. Para asegurarte de que script.js se ejecute una vez que D3 esté listo, agrega defer y muévelo a la sección <head> del documento, justo después del elemento <script> de D3. Ahora ya no bloquea el analizador, y la descarga comienza antes.

<script src="https://d3js.org/d3.v3.min.js" defer></script>
<script src="./script.js" defer></script>

Recursos de terceros de carga diferida

Todos los recursos que se encuentran en la mitad inferior de la página son buenos candidatos para la carga diferida.

La app de ejemplo tiene un video de YouTube incorporado en un iframe. Para ver cuántas solicitudes hace la página y cuáles provienen del iframe incorporado de YouTube:

  1. Para obtener una vista previa del sitio, presiona Ver app. Luego, presiona Pantalla completa pantalla completa.
  2. Presiona "Control + Mayús + J" (o bien "Comando + Opción + J" en Mac) para abrir Herramientas para desarrolladores.
  3. Haga clic en la pestaña Red.
  4. Selecciona la casilla de verificación Inhabilitar caché.
  5. Selecciona 3G rápida en el menú desplegable Regulación.
  6. Vuelve a cargar la página.

Captura de pantalla del panel Network de Herramientas para desarrolladores.

El panel Red revela que la página realizó un total de 28 solicitudes y transfirió casi 1 MB de recursos comprimidos.

Para identificar las solicitudes que realizó el iframe de YouTube, busca el ID de video 6lfaiXM6waw en la columna Iniciador. Para agrupar todas las solicitudes por dominio, sigue estos pasos:

  • En el panel Red, haz clic con el botón derecho en el título de una columna.

  • En el menú desplegable, selecciona la columna Domains.

  • Para ordenar las solicitudes por dominio, haz clic en el título de la columna Dominios.

El nuevo orden revela que hay solicitudes adicionales para los dominios de Google. En total, el iframe de YouTube realiza 14 solicitudes de secuencias de comandos, hojas de estilo, imágenes y fuentes. Sin embargo, a menos que los usuarios realmente se desplacen hacia abajo para reproducir el video, en realidad no necesitan todos esos elementos.

Puedes esperar a que el video se cargue de forma diferida hasta que el usuario se desplace hacia abajo hasta esa sección de la página para reducir la cantidad de solicitudes que la página realiza inicialmente. Con este enfoque, se guardan los datos de los usuarios y se acelera la carga inicial.

Una forma de implementar la carga diferida es con Intersection Observer, una API del navegador que te notifica cuando un elemento ingresa al viewport del navegador o sale de él.

Paso 1: Evita que el video se cargue inicialmente

Para realizar una carga diferida del iframe de video, primero debes evitar que se cargue de la manera habitual. Para ello, reemplaza el atributo src con el atributo data-src para especificar la URL del video:

<iframe width="560" height="315" data-src="https://www.youtube.com/embed/lS9D6w1GzGY" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

data-src es un atributo de datos que te permite almacenar información adicional sobre elementos HTML estándar. Un atributo de datos puede tener cualquier nombre, siempre que empiece con "data-".

Un iframe sin un src simplemente no se cargará.

Paso 2: Usa Intersection Observer para realizar una carga diferida del video

Para cargar un video cuando un usuario se desplaza hacia él, debes saber cuándo sucede eso. Ahí es donde entra en juego la API de Intersection Observer. La API de Intersection Observer te permite registrar una función de devolución de llamada que se ejecuta cada vez que un elemento al que deseas realizar un seguimiento entra en el viewport o sale de él.

Para comenzar, crea un archivo nuevo y asígnale el nombre lazy-load.js:

  • Haz clic en New File y asígnale un nombre.
  • Haz clic en Agregar este archivo.

Agrega la etiqueta de la secuencia de comandos al encabezado del documento:

 <script src="/lazy-load.js" defer></script>

En lazy-load.js, crea un nuevo IntersectionObserver y pásale una función de devolución de llamada para ejecutar:

// create a new Intersection Observer
let observer = new IntersectionObserver(callback);

Ahora, proporciona a observer un elemento de destino para mirar (el iframe de video en este caso). Para ello, pásalo como un argumento en el método observe:

// the element that you want to watch
const element = document.querySelector('iframe');

// register the element with the observe method
observer.observe(element);

callback recibe una lista de objetos IntersectionObserverEntry y el objeto IntersectionObserver en sí. Cada entrada contiene un elemento target y propiedades que describen sus dimensiones, su posición, la hora en que ingresó al viewport y mucho más. Una de las propiedades de IntersectionObserverEntry es isIntersecting, un valor booleano que equivale a true cuando el elemento ingresa al viewport.

En este ejemplo, la target es la iframe. isIntersecting es igual a true cuando target ingresa al viewport. Para ver esto en acción, reemplaza callback por la siguiente función:

let observer = new IntersectionObserver(callback);
let observer = new IntersectionObserver(function(entries, observer) {
    entries.forEach(entry => {
      console.log(entry.target);
      console.log(entry.isIntersecting);
    });
  });
  1. Para obtener una vista previa del sitio, presiona Ver app. Luego, presiona Pantalla completa pantalla completa.
  2. Presiona "Control + Mayús + J" (o bien "Comando + Opción + J" en Mac) para abrir Herramientas para desarrolladores.
  3. Haz clic en la pestaña Consola.

Intenta desplazarte hacia arriba y hacia abajo. Deberías ver el valor del cambio de isIntersecting y el elemento de destino registrado en la consola.

Si deseas cargar el video cuando el usuario se desplaza hasta su posición, usa isIntersecting como condición para ejecutar una función loadElement, que obtiene el valor del data-src del elemento iframe y lo establece como el atributo src del elemento iframe. Ese reemplazo activa la carga del video. Luego, una vez que se cargue el video, llama al método unobserve en observer para dejar de mirar el elemento de destino:

let observer = new IntersectionObserver(function (entries, observer) {
  entries.forEach(entry => {
    console.log(entry.target);
    console.log(entry.isIntersecting);
  });
});
    if (entry.isIntersecting) {
      // do this when the element enters the viewport
      loadElement(entry.target);
      // stop watching
      observer.unobserve(entry.target);
    }
  });
});

function loadElement(element) {
  const src = element.getAttribute('data-src');
  element.src = src;
}

Paso 3: Reevalúa el rendimiento

Para ver cómo se modificó el tamaño y la cantidad de recursos, abre el panel Red de Herramientas para desarrolladores y vuelve a cargar la página. El panel Red revela que la página realizó 14 solicitudes y solo 260 KB. Es una mejora significativa.

Ahora desplázate hacia abajo en la página y observa el panel Network. Cuando llegues al video, deberías ver que la página activa solicitudes adicionales.

Establece una conexión previa con los orígenes necesarios

Diferiste el código JavaScript no crítico y cargaste de forma diferida las solicitudes de YouTube, así que ahora es el momento de optimizar el contenido restante de terceros.

Agregar el atributo rel=preconnect a un vínculo le indica al navegador que establezca una conexión con un dominio antes de que se realice la solicitud del recurso. Este atributo se utiliza mejor en orígenes que proporcionan recursos que estás seguro de que la página necesita.

La auditoría de Lighthouse que realizaste en el primer paso que se sugirió en Preconnect to required origins, que puedes ahorrar alrededor de 400 ms si estableciste conexiones tempranas astaticxx.facebook.com y youtube.com:

Conéctate previamente a la auditoría de orígenes necesaria con el dominio estáticoxx.facebook.com destacado.

Como el video de YouTube ahora se carga de forma diferida, solo salestaticxx.facebook.com, la fuente del widget para compartir contenido en las redes sociales. Establecer una conexión anticipada a este dominio es tan simple como agregar una etiqueta <link> al <head> del documento:

  <link rel="preconnect" href="https://staticxx.facebook.com">

Reevalúa el rendimiento

Este es el estado de la página después de la optimización. Sigue los pasos de la sección Cómo medir el rendimiento del codelab para ejecutar otra auditoría de Lighthouse.

Auditoría de Lighthouse que muestra un FCP de 1 segundo y una puntuación de rendimiento de 99.