Optimización de la página de producto en Magento 2: velocidad donde se convierte

Optimización de la página de producto en Magento 2: velocidad donde se convierte

La página de producto es lo último que hay entre un comprador y el botón de añadir al carrito. También es, en la mayoría de las tiendas Magento, la plantilla más pesada del catálogo: una galería de medios, la lógica de opciones de los configurables, el cálculo del precio, el estado del stock, productos relacionados, reseñas y lo que sea que el equipo de marketing añadió el trimestre pasado.

Las PDP tienen un modo de fallo particular que conviene entender: a menudo se ven bien en Lighthouse y se sienten lentas para los usuarios reales. Es porque las partes caras no son el renderizado inicial: son el JavaScript que arranca después y las interacciones que siguen. Que es exactamente lo que mide el INP.

Si todavía no lo ha hecho, empiece por los cimientos a nivel de sitio y por el trabajo en las páginas de categoría que alimenta de tráfico a estas.

La galería es su elemento LCP

En prácticamente cualquier PDP, el elemento LCP es la imagen principal del producto. Eso convierte a la galería de medios en lo que más palanca tiene en la página.

La galería por defecto de Magento (Fotorama en Luma) se inicializa en JavaScript. La imagen principal no está en el HTML inicial como un <img> normal: se inyecta después de ejecutar el script de la galería. Es un problema estructural de LCP: el navegador no puede empezar a pedir su imagen más importante hasta haber descargado, parseado y ejecutado una biblioteca de JavaScript.

La solución es renderizar la imagen principal en el servidor como una etiqueta <img> real dentro del HTML, y dejar que el script de la galería la mejore después. En concreto:

<img
  src="/media/catalog/product/cache/.../product-800.webp"
  srcset="... 480w, ... 800w, ... 1200w"
  sizes="(max-width: 768px) 100vw, 50vw"
  width="800" height="800"
  fetchpriority="high"
  alt="...">

Más un <link rel="preload" as="image"> en el head con un imagesrcset que coincida. Tenga en cuenta que el preload y el srcset deben coincidir exactamente, o el navegador descargará la imagen dos veces.

Los temas basados en Hyvä suelen resolver esto correctamente de forma nativa. En Luma es una sobreescritura de plantilla, y suele ser la mayor mejora individual de LCP disponible en una PDP.

Las miniaturas deben ser diferidas y pequeñas. Una galería con doce imágenes no debería descargar doce archivos a tamaño completo al cargar. Cargue en diferido todo lo que va después de la primera, y asegúrese de que las dimensiones de miniatura de su view.xml coinciden con el tamaño renderizado.

Los productos configurables y la carga de JSON

Esta es la trampa de rendimiento más silenciosa de Magento.

Para un producto configurable, Magento serializa toda la matriz de opciones en la página como JSON: el ID, el precio, el conjunto de imágenes y la combinación de atributos de cada producto simple. En una camiseta con 5 tallas y 4 colores son 20 entradas y nadie lo nota. En un producto con 6 atributos y 800 variantes, el JSON incrustado puede llegar a varios cientos de kilobytes de HTML que cada visitante descarga y que cada parser tiene que masticar.

Síntomas: documentos HTML anormalmente grandes, tiempos de parseo lentos y problemas de INP al hacer clic en los swatches, porque el navegador está filtrando una estructura enorme en memoria en cada interacción.

Opciones, más o menos por orden de esfuerzo:

  • Reduzca los datos de imágenes. Por defecto cada variante lleva su propia carga de galería. Si las variantes comparten imágenes, configurarlas para que las hereden reduce el JSON de forma drástica.
  • Recorte lo que se serializa. Un plugin en el bloque del configurable puede eliminar los campos que su tema nunca lee.
  • Cargue los datos de las variantes de forma asíncrona. Para matrices realmente grandes, pida los datos de opciones a través de un endpoint ligero después del renderizado inicial, en lugar de incrustarlos. Es una tarea de desarrollo real, pero es la respuesta correcta por encima de cierto número de variantes.

Conviene medir antes de dar nada por hecho: vea el código fuente de su producto configurable más grande y compruebe el tamaño del documento. Si pasa de 500 KB, ese es su problema.

Precio, stock y lo que rompe la full-page cache

Las PDP tienen que mostrar precios específicos por cliente y stock en vivo, dos cosas dinámicas en una página que quiere completamente cacheada.

Magento lo resuelve con el contenido privado, y funciona, pero el coste aparece en la petición /customer/section/load. En una PDP esa petición suele llevar el contenido del carrito, el estado de la lista de deseos, los productos vistos recientemente y todo lo que hayan registrado sus extensiones.

Dos cosas que revisar:

Productos vistos recientemente. Esta sección crece a medida que el comprador navega y se serializa en cada página. En una sesión larga se convierte en una carga considerable para una función de valor comercial modesto. Mida si se gana el sitio.

Secciones de extensiones. Audite cada sections.xml de su código. Encontramos con regularidad tres o cuatro extensiones que añaden cada una una sección que se dispara en cada carga de página para dar soporte a una función usada en una sola plantilla.

Si mostrar el stock es la única razón por la que un bloque no es cacheable, plantéese si «En stock / Agotado» tiene que ser en tiempo real o si un TTL corto es aceptable. Los recuentos exactos de stock rara vez justifican abrir un agujero en su caché.

Los swatches y el problema del INP

Los clics en swatches son la interacción más habitual en una PDP, lo que los convierte en un contribuyente principal al INP. Cuando un comprador hace clic en un color, Magento normalmente: filtra el JSON de opciones, actualiza el bloque de precio, cambia la galería y actualiza la URL.

Si toda esa cadena se ejecuta de forma síncrona en el hilo principal con un conjunto grande de opciones, aparece un retraso visible, y el INP, que mide todas las interacciones y no solo la primera, lo registrará.

Lo que ayuda:

  • Reducir el JSON de opciones (ver arriba): la mayor parte del retraso de los swatches viene de ahí.
  • Actualizar la imagen de la galería cambiando el src, no reinicializando el componente de galería.
  • Precargar la imagen principal solo de la variante por defecto; dejar que las demás carguen bajo demanda.
  • Reservar dimensiones en el bloque de precio, para que un cambio de precio no desplace el layout: el CLS provocado por swatches es habitual, y Google cuenta los desplazamientos de layout que ocurren después de una interacción.

Por debajo del pliegue: difiera con agresividad

Los productos relacionados, los upsells, los cross-sells, las reseñas y los bloques de vistos recientemente se renderizan en el servidor por defecto. Cada uno es una consulta de colección de productos aparte, en una página que ya tiene trabajo de sobra.

El patrón que funciona: renderizarlos en diferido a través de una pequeña petición asíncrona disparada por proximidad de scroll o, como mínimo, asegurarse de que no bloquean la ruta crítica. En el caso concreto de los bloques de productos relacionados, compruebe si la regla que los genera está haciendo algo caro: las extensiones de productos relacionados dinámicos a veces ejecutan consultas sin índice en cada vista de página.

Las reseñas merecen mención aparte. Los widgets de reseñas de terceros están entre los peores infractores en las PDP de Magento: normalmente inyectan un script síncrono, se renderizan en un contenedor sin altura reservada (CLS) y añaden trabajo al hilo principal (INP). Cárguelos de forma asíncrona, reserve el espacio y exija a su proveedor de reseñas los mismos estándares que a su propio código.

Scripts de terceros

La PDP es donde se acumulan las etiquetas de marketing: analítica, mapas de calor, widgets de chat, personalización, A/B testing, píxeles de retargeting. Cada una añade ejecución en el hilo principal.

Las herramientas de A/B testing merecen un escrutinio especial, porque muchas están diseñadas para cargar de forma síncrona y bloqueante para evitar parpadeos, lo que significa que se ponen directamente delante de su LCP. Si tiene una, sepa lo que le cuesta.

Enfoque práctico: consiga un inventario completo de etiquetas desde su tag manager, asigne a cada una un coste medido (el panel Performance de Chrome DevTools, filtrado por origen del script) y lleve esa lista a quien sea responsable del stack de marketing. La conversación va mejor con números que con opiniones.

Datos estructurados, porque la página tiene que ser encontrada

No es rendimiento en sentido estricto, pero el trabajo en la PDP es el momento natural para hacerlo. El schema de Product con offers, aggregateRating y availability genera resultados enriquecidos en los buscadores: el precio y el stock mostrados directamente en la página de resultados. Magento emite un schema de producto básico, pero con frecuencia está incompleto o roto por sobreescrituras del tema.

Valídelo con la prueba de resultados enriquecidos de Google sobre una URL de producto real, no una de muestra. Arreglar un schema roto es barato, y la mejora en el porcentaje de clics suele ser mayor que la ganancia de conversión del trabajo de rendimiento que tiene al lado.

La lista corta

  1. Renderice la imagen principal del producto como HTML del servidor con preload y fetchpriority="high".
  2. Compruebe el tamaño del JSON de sus configurables. Arréglelo si es grande.
  3. Audite sections.xml y recorte el contenido privado.
  4. Difiera los productos relacionados, los upsells y las reseñas.
  5. Haga inventario de los scripts de terceros y póngales precio uno a uno.
  6. Valide los datos estructurados de producto.

El orden importa. El arreglo de la galería afecta a todos los visitantes; la auditoría de scripts de terceros suele encontrar más milisegundos en total, pero requiere el acuerdo de otras personas. Haga lo primero mientras negocia lo segundo.

Siguiente en esta serie: rendimiento del checkout, donde las apuestas suben y el JavaScript empeora.


Emyrix construye y optimiza tiendas en Magento y Adobe Commerce, incluido el trabajo en la PDP que convierte el tráfico del catálogo en pedidos. Escríbanos si quiere que echemos un vistazo a la suya.

Preguntas frecuentes

¿Por qué las páginas de producto de Magento se ven bien en Lighthouse pero se sienten lentas?

Las partes caras no son el renderizado inicial: son el JavaScript que arranca después y las interacciones que siguen, que es exactamente lo que mide el INP. A Lighthouse se le puede escapar, mientras los usuarios reales notan el retraso de los swatches y el coste de arranque.

¿Cómo arreglo el LCP en una página de producto de Magento?

La galería Fotorama por defecto de Luma inyecta la imagen principal después de ejecutar su script, así que el navegador no puede pedir su imagen más importante hasta haber descargado y ejecutado una biblioteca de JavaScript. Renderice la imagen principal en el servidor como una etiqueta img real, con un preload que coincida y fetchpriority high: suele ser la mayor mejora de LCP disponible en una PDP.

¿Por qué el HTML de mi página de producto configurable es tan grande?

Magento serializa toda la matriz de opciones en la página como JSON: el ID, el precio, el conjunto de imágenes y la combinación de atributos de cada producto simple. En un producto con 6 atributos y 800 variantes el JSON incrustado puede llegar a varios cientos de kilobytes, así que vea el código fuente de su configurable más grande y, si pasa de 500 KB, ese es su problema.

¿Los recuentos de stock en tiempo real necesitan romper la full-page cache en una PDP?

Rara vez. Si mostrar el stock es la única razón por la que un bloque no es cacheable, plantéese si «En stock» o «Agotado» tiene que ser en tiempo real o si un TTL corto es aceptable, porque los recuentos exactos de stock rara vez justifican abrir un agujero en su caché.

Trabaje con Emyrix

¿Quiere saber cómo está su propia tienda?

Envíenos la URL y un ingeniero de Magento la revisará (versión y estado de parches, velocidad, cache, indexación, checkout) y después le enviará por correo lo que encontró. Gratis, y sin ninguna obligación.