Optimiza JavaScript de terceros

Las secuencias de comandos de terceros afectan el rendimiento, por lo que es importante auditarlas con frecuencia 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:

  • Aplaza la carga de la secuencia de comandos

  • Carga diferida de recursos no esenciales

  • Conexión previa a los orígenes necesarios

La app de ejemplo incluida incluye una página web simple con tres funciones que provienen de fuentes externas:

  • Una incorporación de video

  • Una 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 los recursos de terceros destacados.
Recursos de terceros en la app de ejemplo.

Empezarás por medir el rendimiento de la app y, luego, aplicarás cada técnica para mejorar diferentes aspectos del rendimiento de la app.

Cómo medir el rendimiento

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

  1. Haz clic en Remix para editar 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 un rendimiento de referencia:

  1. Presiona "Control + Mayúsculas + J" (o "Comando + Opción + J" en Mac) para abrir DevTools.
  2. Haz clic en la pestaña Lighthouse.
  3. Haz clic en 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 Red 3G rápida simulada, CPU 4 veces más lenta.
  6. Selecciona la casilla de verificación Borrar almacenamiento.
  7. Haz clic en Run audits.

Cuando ejecutas una auditoría en tu máquina, los resultados exactos pueden variar, pero deberías notar que el tiempo de primer procesamiento de imagen con contenido (FCP) es bastante alto y que Lighthouse sugiere dos oportunidades para investigar: Eliminar los recursos que bloquean la renderización y Establecer una conexión previa con los orígenes requeridos. (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 que muestra un FCP de 2.4 segundos y dos oportunidades: Eliminar recursos que bloquean la renderización y Conectarse previamente a los orígenes requeridos.

Aplaza el JavaScript de terceros

La auditoría Elimina los recursos que bloquean la renderización identificó que puedes ahorrar tiempo aplazando una secuencia de comandos que proviene de d3js.org:

Captura de pantalla de la auditoría Elimina los recursos que bloquean la 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 adjuntarlo a la página. El orden de las operaciones es importante: script.js debe ejecutarse después de que se analice el documento y se cargue la biblioteca de D3, por lo que se incluye justo antes de la etiqueta </body> de cierre en index.html.

Sin embargo, la secuencia de comandos de 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 secuencia de comandos destacada en el encabezado.

Dos atributos mágicos pueden desbloquear el analizador cuando se agregan a la etiqueta de secuencia de comandos:

  • async garantiza que las secuencias de comandos se descarguen en segundo plano y se ejecuten a 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 se complete el análisis.

Dado que este gráfico no es realmente fundamental para la página en general y es probable que esté debajo de la mitad inferior de la página, usa defer para asegurarte de que no haya bloqueos 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 que el orden de las operaciones sea el correcto

Ahora que D3 se aplazó, 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 después de que D3 esté listo, agrégale defer y muévelo a la <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>

Carga diferida de recursos de terceros

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 verificar cuántas solicitudes realiza la página y cuáles provienen del iframe de YouTube incorporado, haz lo siguiente:

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

Captura de pantalla del panel Network de DevTools.

El panel Network 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 del video 6lfaiXM6waw en la columna Initiator. 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 Dominios.

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

La nueva clasificación revela que hay solicitudes adicionales a 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 se desplacen hacia abajo para reproducir el video, no necesitan todos esos recursos.

Si esperas para cargar el video de forma diferida hasta que un usuario se desplace hasta esa sección de la página, reducirás la cantidad de solicitudes que realiza la página inicialmente. Este enfoque ahorra los datos de los usuarios y acelera la carga inicial.

Una forma de implementar la carga diferida es con el Observador de intersecciones, una API del navegador que te notifica cuando un elemento entra o sale del viewport del navegador.

Paso 1: Evita que el video se cargue inicialmente

Para cargar de forma diferida el iframe de video, primero debes evitar que se cargue de la forma habitual. Para ello, reemplaza el atributo src por 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 en elementos HTML estándar. Se puede asignar cualquier nombre a un atributo de datos, siempre y cuando comience con "data-".

Un iframe sin un src simplemente no se cargará.

Paso 2: Usa Intersection Observer para cargar el video de forma diferida

Para cargar el video cuando un usuario se desplaza hasta él, debes saber cuándo sucede eso. Aquí 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 del que deseas hacer un seguimiento entra o sale del viewport.

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

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

Agrega la etiqueta de secuencia de comandos al encabezado de tu 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 que se ejecute:

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

Ahora, asigna a observer un elemento de destino para que lo mire (el iframe de video en este caso) pasándolo como 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, 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 es igual a true cuando el elemento entra en el viewport.

En este ejemplo, target es iframe. isIntersecting es igual a true cuando target entra en el viewport. Para ver cómo funciona, 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úsculas + J" (o "Comando + Opción + J" en Mac) para abrir DevTools.
  3. Haz clic en la pestaña Consola.

Intenta desplazarte hacia arriba y hacia abajo. Deberías ver que cambia el valor de isIntersecting y que el elemento de destino se registra en la consola.

Para 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: Vuelve a evaluar el rendimiento

Para ver cómo cambiaron el tamaño y la cantidad de recursos, abre el panel Red de DevTools y vuelve a cargar la página. El panel Red revela que la página realizó 14 solicitudes y solo 260 KB. Esa es una mejora significativa.

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

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

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

Si agregas el atributo rel=preconnect a un vínculo, le indicas al navegador que establezca una conexión con un dominio antes de que se realice la solicitud de ese recurso. Este atributo se usa mejor en orígenes que proporcionan recursos que sabes que la página necesita.

La auditoría de Lighthouse que ejecutaste en el primer paso sugerido en Cómo establecer una conexión previa a los orígenes requeridos indica que puedes ahorrar alrededor de 400 ms si estableces conexiones anticipadas a staticxx.facebook.com y youtube.com:

Auditoría de conexión previa a los orígenes requeridos con el dominio staticxx.facebook.com destacado.

Dado que el video de YouTube ahora se carga de forma diferida, solo queda staticxx.facebook.com, la fuente del widget de uso compartido en 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">

Vuelve a evaluar el rendimiento

Este es el estado de la página después de la optimización. Sigue los pasos de la sección Mide 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.