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 completapantalla 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 `Command + Option + J` en Mac) para abrir Herramientas para desarrolladores.
  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, que bloquea el análisis del documento resto:

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 en la primera oportunidad después de que finalicen la descarga.

  • defer garantiza que las secuencias de comandos se descarguen en segundo plano y se ejecuten después de que el análisis esté completamente terminado.

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. Ya no bloqueará el analizador, y la descarga comenzará 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 aplicación 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 completapantalla 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 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 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.

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

Si esperas la carga diferida del video hasta que un usuario se desplace hacia abajo a esa sección de la página, reduces 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 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 realizar una carga diferida en el iframe de video, primero debes evitar que se cargue de la manera 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 carga.

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 que deseas rastrear 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 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. 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 ingresa al 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 completapantalla 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 en 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">

Volver 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.