Optimiza JavaScript de terceros

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

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

  • Carga diferida de recursos no críticos

  • Realiza una conexión previa a los orígenes necesarios

La app de ejemplo incluida presenta 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 recursos de terceros destacados.
Recursos de terceros en la app de ejemplo.
.

Comenzarás por medir el rendimiento de la app 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 para editar para que el proyecto se pueda editar.
  2. Para obtener una vista previa del sitio, presiona Ver app. Luego, presiona Pantalla completa pantalla completa

Ejecuta una auditoría de rendimiento 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 Dispositivos móviles.
  4. Selecciona la casilla de verificación Rendimiento. (Puedes borrar 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 Slowdown de CPU.
  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 que Lighthouse sugiere dos oportunidades para investigar: Eliminar los recursos que bloquean la renderización y Preconectar a los orígenes requeridos. (Incluso si todas las métricas están en verde, las optimizaciones producirán mejoras de todas formas).

Captura de pantalla de la auditoría de Lighthouse que muestra un FCP de 2.4 segundos y dos oportunidades: Elimina los recursos que bloquean la renderización y la opción Preconnect para los orígenes requeridos.

Difiere el código JavaScript de terceros

En la auditoría Elimina los recursos que bloquean el procesamiento, se identificó que puedes ahorrar tiempo si aplazas una secuencia de comandos proveniente de d3js.org:

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

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

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

Dos atributos mágicos pueden desbloquear el analizador cuando se agregan 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 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 crítico para la página general y lo más probable es que aparezca en la mitad inferior de la página, usa defer para asegurarte de que no haya bloqueado el 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 las operaciones tengan el orden correcto

Ahora que D3 se aplaza, script.js se ejecutará antes de que D3 esté lista, 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, agrega defer y muévelo hasta 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>

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 aplicación de ejemplo tiene un video de YouTube incorporado en un iframe. Para ver 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 `Command + Option + 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 Initiator. Para agrupar todas las solicitudes por dominio, haz lo siguiente:

  • 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. Este enfoque ahorra a los usuarios y acelera la carga inicial.

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

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 que comience con "data-".

Un iframe sin un src simplemente no se carga.

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

Para cargar un video cuando un usuario se desplaza hasta él, debes saber cuándo sucede. 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 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 Archivo nuevo y asígnale un nombre.
  • Haz clic en Agregar este archivo.

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

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

En lazy-load.js, crea un nuevo elemento 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, pásale a observer un elemento de destino para mirar (en este caso, el iframe de video). Para ello, pásalo 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, 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 es igual a true cuando el elemento ingresa al viewport.

En este ejemplo, target es 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úsculas + J` (o `Command + Option + 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 cambio de isIntersecting y el elemento de destino registrado en la consola.

Para cargar el video cuando el usuario se desplaza a 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 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 consulta 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 JavaScript no fundamental y cargaste de forma diferida las solicitudes de YouTube, por lo que es 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 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 Preconectar a los orígenes requeridos, que puedes ahorrar alrededor de 400 ms si establecis conexiones anticipadas constaticxx.facebook.com y youtube.com:

Establece una conexión previa en la auditoría de orígenes obligatoria con el dominio estáticoxx.facebook.com destacado.

Dado que el video de YouTube ahora se carga de forma diferida, solo se deja estáticaxx.facebook.com, la fuente del widget para compartir contenido 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.