Mejorando el LCP usando WebP

Guille Paz


El post fue actualizado en base a un comentario de @javiperezrequejo.

En los ratos libres vengo trabajando en optimizar la performance de XStore, el side project para explorar el catálogo de juegos de Xbox.

Actualmente está construido con una arquitectura 100% client-side y sin usar ningún framework, es todo vanilla. A pesar de ser client-side rendering performa muy bien y con buenos valores para los Core Web Vitals, salvo para el LCP que está en casi 3 segundos.

En este caso el LCP son las imágenes de los juegos y vienen de una API que consumo de Microsoft de la cual no soy dueño.

Las imágenes son el recurso más pesado de la web cuando no se optimizan y, en mi caso, al no ser el dueño no tengo control para optimizarlas. Pero... ¿no tengo control para optimizarlas?

¡Sí, lo tengo! Para ello, armé un "servicio" que me permite optimizar las imágenes que me devuelve la API de Microsoft y entregar WebP en vez de JPG.

El formato de imágenes WebP es más liviano que JPEG / PNG y tiene muy buen soporte en los navegadores modernos. Recomiendo usar WebP siempre que se pueda.

La imagen de la izquierda está sin optimizar y pesa 304KB, mientras que la de la derecha está optimizada y pesa 140KB.

De esta forma, pude mejorar el LCP de 2.6s a 2.3s y bajar el peso de todas las imágenes que se cargan.

El "servicio" es muy simple (unas 20 líneas) gracias a la librería sharp y, básicamente, actúa como proxy yendo a buscar la imagen original, la optimiza y la sirve con el formato WebP ⚡️.

Actualizado. Es importante tener en cuenta el header accept para validar que el dispositivo que pide la imagen soporte image/webp. Si lo soporta se entrega webp, sino entrega una versión optimizada de jpeg.

const axios = require('axios');
const sharp = require('sharp');

module.exports = async (req, res) => {
  const path = req.query.path;
  delete req.query.path;

  const queryString = new URLSearchParams(req.query).toString();
  const microsoft = `https://store-images.s-microsoft.com/image/${path}?${queryString}`;
  const response = await axios.get(microsoft, { responseType: 'arraybuffer' });

  const format = req.headers.accept.includes('image/webp') ? 'webp' : 'jpeg';

  const data = await sharp(response.data)
    [format]({ quality: 80 })
    .toBuffer();

  res.setHeader('Content-Type', `image/${format}`);
  res.setHeader('Content-Length', data.length);
  res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');

  return res.status(200).send(data);
};

Todo recurso se puede optimizar no importa quién sea su dueño.

Chao. 🚀


Foto de cover de https://web.dev/optimize-lcp/.