Un paquete de Next.js para administrar bibliotecas de terceros

En 2021, el equipo de Chrome Aurora presentó el componente de secuencia de comandos para mejorar el rendimiento de carga de secuencias de comandos de terceros en Next.js. Desde su lanzamiento, expandimos sus capacidades para que los desarrolladores carguen recursos de terceros de forma más fácil y rápida.

En esta entrada de blog, se proporciona una descripción general de las funciones más recientes que lanzamos, en particular la biblioteca @next/third-parties, y un resumen de iniciativas futuras en nuestra hoja de ruta.

Implicaciones en el rendimiento de las secuencias de comandos de terceros

El 41% de todas las solicitudes de terceros en los sitios de Next.js son secuencias de comandos. A diferencia de otros tipos de contenido, las secuencias de comandos pueden tardar una cantidad considerable de tiempo en descargarse y ejecutarse, lo que puede bloquear la renderización y retrasar la interactividad del usuario. Los datos del Informe sobre la experiencia del usuario de Chrome (CrUX) muestran que los sitios de Next.js que cargan más secuencias de comandos de terceros tienen porcentajes de aprobación más bajos de Interaction to Next Paint (INP) y Largest Contentful Paint (LCP).

Gráfico de barras que muestra una disminución en el porcentaje de Next.js que logra buenas puntuaciones de INP y LCP en proporción a la cantidad de terceros cargados
Informe de CrUX de diciembre de 2023 (110,823 sitios)

La correlación observada en este gráfico no implica causalidad. Sin embargo, los experimentos locales proporcionan evidencia adicional de que las secuencias de comandos de terceros afectan significativamente el rendimiento de la página. Por ejemplo, en el siguiente gráfico, se comparan varias métricas de Labs cuando se agrega un contenedor de Google Tag Manager, que incluye 18 etiquetas seleccionadas de forma aleatoria, a Taxonomy, una app de ejemplo popular de Next.js.

Gráfico de barras que muestra la diferencia en varias métricas de lab cuando un sitio se carga con y sin Google Tag Manager
WebPageTest (móvil 4G - Virginia, EE.UU.)

En la documentación de WebPageTest, se proporcionan detalles sobre cómo se miden estos tiempos. De un vistazo, es claro que todas estas métricas de lab se ven afectadas por el contenedor de GTM. Por ejemplo, el tiempo de bloqueo total (TBT), un proxy de lab útil que se aproxima a la INP, aumentó casi 20 veces.

Componente de secuencia de comandos

Cuando enviamos el componente <Script> en Next.js, nos aseguramos de presentarlo a través de una API fácil de usar que se asemeja mucho al elemento <script> tradicional. Cuando lo usan, los desarrolladores pueden ubicar en conjunto una secuencia de comandos de terceros en cualquier componente de su aplicación, y Next.js se encargará de secuenciar la secuencia de comandos después de que se carguen los recursos críticos.

<!-- By default, script will load after page becomes interactive -->
<Script src="https://example.com/sample.js" />

<!-- Script is injected server-side and fetched before any page hydration occurs -->
<Script strategy=”beforeInteractive” src="https://example.com/sample.js" />

<!-- Script is fetched later during browser idle time -->
<Script strategy=”lazyOnload” src="https://example.com/sample.js" />

Decenas de miles de aplicaciones de Next.js, incluidos sitios populares como Patreon, Target y Notion, usan el componente <Script>. A pesar de su efectividad, algunos desarrolladores expresaron su preocupación por lo siguiente:

  • Dónde colocar el componente <Script> en una app de Next.js y cumplir con las diferentes instrucciones de instalación de los distintos proveedores externos (experiencia del desarrollador)
  • Cuál es la estrategia de carga más óptima para usar en diferentes secuencias de comandos de terceros (experiencia del usuario).

Para abordar ambas inquietudes, lanzamos @next/third-parties, una biblioteca especializada que ofrece un conjunto de componentes y utilidades optimizados adaptados para terceros populares.

Experiencia del desarrollador: Facilita la administración de bibliotecas de terceros

Se usan muchas secuencias de comandos de terceros en un porcentaje significativo de sitios de Next.js, y Google Tag Manager es la más popular, ya que la usa el 66% de los sitios, respectivamente. @next/third-parties se basa en el componente <Script> introduciendo wrappers de nivel superior diseñados para simplificar el uso en estos casos de uso comunes.

import { GoogleAnalytics } from "@next/third-parties/google";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
      <GoogleTagManager gtmId="GTM-XYZ" />
    </html>
  );
}

Google Analytics, otra secuencia de comandos de terceros muy utilizada (52% de los sitios de Next.js), también tiene su propio componente dedicado.

import { GoogleAnalytics } from "@next/third-parties/google";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
      <GoogleAnalytics gaId="G-XYZ" />
    </html>
  );
}

@next/third-parties simplifica el proceso de carga de secuencias de comandos de uso general, pero también extiende nuestra capacidad para desarrollar utilidades para otras categorías de terceros, como las incorporaciones. Por ejemplo, las incorporaciones de Google Maps y YouTube se usan en el 8% y el 4% de los sitios web de Next.js, respectivamente, y también enviamos componentes para que sean más fáciles de cargar.

import { GoogleMapsEmbed } from "@next/third-parties/google";
import { YouTubeEmbed } from "@next/third-parties/google";

export default function Page() {
  return (
    <>
      <GoogleMapsEmbed
        apiKey="XYZ"
        height={200}
        width="100%"
        mode="place"
        q="Brooklyn+Bridge,New+York,NY"
      />
      <YouTubeEmbed videoid="ogfYd705cRs" height={400} params="controls=0" />
    </>
  );
}

Experiencia del usuario: Se hace que las bibliotecas de terceros se carguen más rápido.

En un mundo ideal, cada biblioteca de terceros ampliamente adoptada estaría optimizada por completo, por lo que no sería necesaria ninguna abstracciones que mejoraran su rendimiento. Sin embargo, hasta que eso se convierta en una realidad, podemos intentar mejorar su experiencia del usuario cuando se integra a través de frameworks populares como Next.js. Podemos experimentar con diferentes técnicas de carga, asegurarnos de que las secuencias de comandos se ordenen de la manera correcta y, en última instancia, compartir nuestros comentarios con proveedores externos para fomentar los cambios en upstream.

Por ejemplo, las incorporaciones de YouTube. En el que algunas implementaciones alternativas tienen un rendimiento mucho mejor que la incorporación nativa. Actualmente, el componente <YouTubeEmbed> que exporta @next/third-parties usa lite-youtube-embed, que, cuando se muestra en una comparación de "Hello, World" en Next.js, se carga considerablemente más rápido.

GIF que muestra la comparación de carga de página entre el componente de incorporación de YouTube y un iframe normal de YouTube
WebPageTest (móvil 4G - Virginia, EE.UU.)

Del mismo modo, en el caso de Google Maps, incluimos loading="lazy" como atributo predeterminado de la incorporación para garantizar que el mapa solo se cargue cuando se encuentre a una distancia determinada del viewport. Incluir este atributo puede parecer obvio, especialmente porque la documentación de Google Maps lo incluye en su fragmento de código de ejemplo, pero solo el 45% de los sitios de Next.js que incorporan Google Maps utilizan loading="lazy".

Cómo ejecutar secuencias de comandos de terceros en un trabajador web

Una técnica avanzada que estamos explorando en @next/third-parties es facilitar la transferencia de las secuencias de comandos de terceros a un trabajador web. Esto, que se popularizó gracias a bibliotecas como Partytown, puede reducir en gran medida el impacto de las secuencias de comandos de terceros en el rendimiento de la página, ya que las traslada por completo del subproceso principal.

En el siguiente GIF animado, se muestran las variaciones en las tareas largas y el tiempo de bloqueo del subproceso principal cuando se aplican diferentes estrategias de <Script> a un contenedor de GTM dentro de un sitio de Next.js. Ten en cuenta que, si bien cambiar entre las opciones de estrategia solo retrasa el momento en que se ejecutan estas secuencias de comandos, trasladarlas a un trabajador web elimina por completo su tiempo en el subproceso principal.

GIF que muestra las diferencias en el tiempo de bloqueo del subproceso principal para las diferentes estrategias de secuencia de comandos
WebPageTest (móvil 4G - Virginia, EE.UU.)

En este ejemplo en particular, mover la ejecución del contenedor de GTM y las secuencias de comandos de sus etiquetas asociadas a un trabajador web redujo el TBT en un 92%.

Vale la pena señalar que, si no se administra con cuidado, esta técnica puede interrumpir de forma silenciosa muchas secuencias de comandos de terceros, lo que dificulta la depuración. En los próximos meses, validaremos si algún componente de terceros que ofrece @next/third-parties funciona correctamente cuando se ejecuta en un trabajador web. Si es así, trabajaremos para proporcionar una forma fácil y opcional para que los desarrolladores usen esta técnica.

Próximos pasos

En el proceso de desarrollo de este paquete, se hizo evidente que era necesario centralizar las recomendaciones de carga de terceros para que otros frameworks también pudieran beneficiarse de las mismas técnicas subyacentes que se usaron. Esto nos llevó a compilar Third Party Capital, una biblioteca que usa JSON para describir técnicas de carga de terceros, que actualmente sirve como base para @next/third-parties.

Como próximos pasos, seguiremos enfocándonos en mejorar los componentes proporcionados para Next.js y, además, expandiremos nuestros esfuerzos para incluir utilidades similares en otros frameworks y plataformas de CMS populares. Actualmente, estamos colaborando con los mantenedores de Nuxt y planeamos lanzar utilidades de terceros similares adaptadas a su ecosistema en un futuro cercano.

Si uno de los terceros que usas en tu app de Next.js es compatible con @next/third-parties, instala el paquete y pruébalo. Nos encantaría recibir tus comentarios en GitHub.