Home Blogs Optimización de su sitio web para Lighthouse v6.0
Applications

Optimización de su sitio web para Lighthouse v6.0

About The Author

Outline

Layer0 es uno de los principales contribuyentes al marco de PWA de comercio electrónico de código abierto, React Storefront. A principios de este año, contribuimos con muchas nuevas características y optimizaciones como React Storefront v7, Dos de los más significativos fueron el cambio a Next.js y la eliminación de varias dependencias clave (por ejemplo, MobX) a favor de las nuevas capacidades incorporadas de React para administrar el estado, como el hook useState y la API de contexto. Esto dio lugar a que el tamaño del paquete del navegador se cortara aproximadamente a la mitad.

Esta fue una buena ganancia en ese momento y ayudó a mejorar las puntuaciones de rendimiento de Lighthouse (v5.7) para las aplicaciones típicas de React Storefront desde los años 80 hasta los 90, según lo medido por PageSpeed Insights (PSI). Para el contexto, una puntuación de más de 83 superó al 99% de los 500 mejores sitios web de comercio electrónico en Lighthouse v5.7. No nos dimos cuenta de lo esencial que sería la reducción de paquetes en los próximos meses, cuando Lighthouse v6.0 caería como una bomba y borraría las puntuaciones de rendimiento de todos.

Vea cómo la distribución de las puntuaciones de Lighthouse medidas en PSI para los principales sitios web de comercio electrónico cambió cuando la v6.0 cayó:

En esta publicación, compartimos cómo mejoramos las puntuaciones de Lighthouse v6.0 para React Storefront, pero las técnicas se pueden aplicar a otros frameworks.

Además, es importante tener en cuenta que Google anunció el 28 de mayo de 2020, las métricas específicas que usarán para clasificar los sitios en 2021. La puntuación de rendimiento de Lighthouse no se utilizará, aunque algunos elementos utilizados para determinar esa puntuación, e incluso entonces, no se medirán utilizando una prueba sintética como Lighthouse, sino datos de campo del mundo real del Informe de experiencia de usuario de Chrome (CRUX).

Las nuevas métricas en Lighthouse 6,0: TBT, LCP y CLS

Lighthouse v6.0 introduce varias nuevas métricas de velocidad perceptiva y reformula cómo las métricas generales afectan la puntuación de rendimiento de Lighthouse de una página.

Faro 5,7 Peso Faro 6,0 Peso
Primera pintura contentful (FCP) 20% Primera pintura contentful (FCP) 15%
Índice de velocidad (SI) 27% Índice de velocidad (SI) 15%
Primera pintura significativa (FMP) 7% Pintura Contenida Más Grande (LCP) 25%
Primer Idle de CPU (FCI) 13% Tiempo total de bloqueo (TBT) 15%
Tiempo de Interactividad (TTI) 33% Tiempo de Interactividad (TTI) 15%
- - Cambio de diseño acumulativo (CLS) 5%

Tiempo total de bloqueo (TBT)

El tiempo total de bloqueo es una nueva métrica incluida en Lighthouse v6.0 que mide el tiempo que el análisis y la ejecución de JavaScript bloquea el hilo principal durante la carga de la página. Esta métrica trata a los spas/PWAs modernos y pesados en JavaScript con mucha dureza. Incluso con el recorte del 50% en el tamaño del paquete de React Storefront 7, vimos una caída de 20 a 30 puntos en la puntuación de rendimiento de Lighthouse v6.0 para las páginas de productos, en gran parte debido a la inclusión de TBT como una métrica que influye en el 25% de la puntuación de rendimiento general.

Si está utilizando un framework isomorfo, como Next.js, que admite la representación del lado del servidor, TBT está determinado principalmente por el tamaño del paquete y el tiempo de hidratación. En pocas palabras, la única manera de mejorar TBT es eliminar dependencias, optimizar sus componentes o hacer que su sitio sea más sencillo utilizando menos componentes.

Pintura contentful más grande (LCP)

LCP es una nueva métrica que tiene un peso del 25% sobre la puntuación general de Lighthouse v6.0. LCP tiene como objetivo cuantificar cómo el usuario percibe el rendimiento de carga inicial de la página observando cuánto tiempo tarda el elemento contentful más grande en terminar de pintar. Para la mayoría de los sitios, especialmente en los sitios web de comercio electrónico, el elemento contento más grande es la imagen de héroe. En el caso de las páginas de productos en las aplicaciones React Storefront, el elemento contentful más grande es la imagen principal del producto. Si no está seguro de qué elemento está en su sitio, PSI le dirá:

Para optimizar el LCP, debe asegurarse de que la imagen se carga lo más rápido posible.

Cambio de diseño acumulativo (CLS)

El cambio de diseño acumulativo mide cuánto cambia el diseño de la página durante la carga inicial de la página. El cambio de diseño es causado más comúnmente por las imágenes, que tienden a empujar los elementos a su alrededor a medida que cambian el tamaño para acomodar la imagen una vez que los datos se descargan de la red. El cambio de diseño a menudo se puede eliminar por completo reservando espacio para cada imagen antes de que se cargue. Afortunadamente, el componente Image de React Storefront ya lo hace, por lo que la aplicación de inicio React Storefront cuenta con una puntuación CLS perfecta de 0 fuera de la caja.

Cabe señalar que otros culpables comunes de los CLS pobres son los banners y los popups que aparecen después de que la página está pintada inicialmente. Los usuarios los odian y ahora Lighthouse también lo hace.

Cómo optimizamos React Storefront para Lighthouse v6.0

Cuando probamos por primera vez la página de producto de la aplicación de inicio React Storefront en Lighthouse v6.0, usando PageSpeed Insights, obtuvo una puntuación baja en los años 60:

Para mejorar la puntuación, primero fijamos nuestra mirada en LCP. A los 2,5 segundos, el FCP era alarmantemente alto (llegaremos a eso más tarde), pero la brecha de casi 3 segundos entre el FCP y el LCP se destacó como algo que necesitaba mejoras.

Optimización de LCP

La imagen principal del producto de React Storefront (simplemente un marcador de posición – la caja verde con «Producto 1» en el centro) se representó como una etiqueta img HTML con una URL src que apunta a https://via.placeholder.com. Ese sitio tiene un TTFB decente (unos 150 ms), que podría mejorarse moviéndose a una CDN más rápida como Layer0 CDN-AS-JavaScript. Aún así, dudamos de que 150 ms representaran la brecha de casi 3 segundos entre FCP y LCP. Para eliminar la brecha, enfilamos la imagen usando una URL de datos base64 como esta:

				
					<img decoding="async" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAlgAAA…"/>

				
			

Lo hicimos descargando la imagen principal, convirtiéndola a base64, y rellenándola en el atributo src durante el renderizado del lado del servidor en la nube de JavaScript sin servidor de Layer0. Aquí está la línea en el punto final de la API del producto:

				
					const mainProductImage = result.product.media.full[0]
mainProductImage.src = await getBase64ForImage(mainProductImage.src)
				
			

Y aquí está el código que obtiene y convierte la imagen en base 64:

				
					import fetch from 'node-fetch'

export default async function getBase64ForImage(src) {
  const res = await fetch(src)
  const contentType = res.headers.get('content-type')
  const buffer = await res.buffer()
  return `data:${contentType};base64,${buffer.toString('base64')}`
return `data:${contentType};base64,${buffer.toString('base64')}` return `data:${contentType};base64,${buffer.toString('base64')}` return `data:${contentType};base64,${buffer.toString('base64')}`
}

				
			

Bastante simple y vieja escuela. ¿Y el impacto en la puntuación?

Al bajar el LCP de 5.3s a 2.8s, ¡conseguimos 21 puntos en la puntuación Lighthouse v6.0 de la página! Es un poco inquietante cómo un cambio tan pequeño puede afectar dramáticamente la puntuación de Lighthouse v6.0, pero lo tomaremos. Cabe señalar que todas las métricas varían un poco entre carreras, pero la puntuación general fue consistente en la baja década de 80 Para el contexto, el sitio web de comercio electrónico líder de mayor rendimiento en la v6.0 puntúa 87 según lo medido en la PSI y parece que está directamente fuera de la década de 90, eche un vistazo www.rockauto.com

La brecha entre FCP y LCP mostrada arriba era casi tan grande como lo vimos en varias carreras. La mayoría de las veces la brecha estaba en el rango de 100ms a 300ms. Ocasionalmente FCP y LCP eran los mismos.

Optimización de TBT

A continuación, intentamos mejorar el TBT. Esto fue bastante desafiante. Como se mencionó anteriormente, para mejorar el LCP, es necesario reducir el tamaño de su paquete de JavaScript o mejorar el tiempo de hidratación. Con la mayoría de las aplicaciones, simplemente no es factible comenzar a extraer dependencias para hacer el paquete más pequeño. Las aplicaciones construidas con React Storefront 7 ya se benefician de muchas optimizaciones en tiempo de compilación que minimizan el tamaño del paquete proporcionado por la configuración webpack de Next.js, así como optimizaciones específicas de babel para Material UI. Entonces, ¿dónde podríamos mejorar? Tiempo de hidratación.

Lazy Hydration para la victoria!

Afortunadamente, la comunidad de React Storefront ya había comenzado a trabajar para apoyar la hidratación perezosa antes de que se cayera Lighthouse v6.0. Esto ciertamente nos hizo acelerar nuestros esfuerzos.

En caso de que no lo sepa, la hidratación se refiere a reaccionar tomando el control de los elementos HTML renderizados en el servidor para que puedan volverse interactivos. Los botones se vuelven clickable; carruseles se vuelven deslizables, etc. Cuantos más componentes tenga una página, más tiempo tardará en hidratarse. Los componentes complejos, como el menú principal y el carrusel de imagen del producto, tardan aún más.

La hidratación perezosa implica retrasar la hidratación de ciertos componentes hasta que sea absolutamente necesaria y lo más importante, después de la carga inicial de la página (y después de que se calcula el TBT). La hidratación perezosa puede ser arriesgada. Debe asegurarse de que los elementos de la página estén listos para responder a la entrada del usuario antes de que el usuario intente interactuar con ellos.

Implementar una hidratación perezosa en React Storefront resultó bastante difícil debido a la dependencia de Material UI en CSS-in-JS, que agrega estilos dinámicamente al documento solo después de que los componentes se hidratan. Guardaré los detalles para otro momento. Al final, creamos un componente LazyHydrate que los desarrolladores pueden insertar en cualquier lugar del árbol de componentes para retrasar la hidratación hasta que ocurra un evento específico, como el elemento que se desplaza en la vista o el usuario que toca la pantalla.

Aquí hay un ejemplo donde perezosamente hidratamos el MediaCarousel que muestra las imágenes principales del producto:

				
					 import LazyHydrate from 'react-storefront/LazyHydrate'
<LazyHydrate id="carousel" on="touch">
  <MediaCarousel
    ...
  />
</LazyHydrate> 

				
			

Aplicamos hidratación perezosa a varias áreas de la aplicación, sobre todo:

  • El menú deslizante: Hidratamos esto cuando el usuario toca el botón de la hamburguesa.
  • Todos los controles debajo del pliegue: Incluye los selectores de tamaño, color y cantidad, así como las pestañas de información del producto.
  • El carrusel de imagen principal: Este y el menú principal son probablemente los componentes con más funcionalidad y, por lo tanto, los más caros de hidratar.

Aquí está la puntuación Lighthouse v6.0 con hidratación perezosa aplicada:

La hidratación perezosa redujo el TBT en casi un 40% y recortó el TTI (que tiene un peso del 15% sobre las puntuaciones en v6.0) en 700 ms. Esto obtuvo una ganancia de 6 puntos en la puntuación general de Lighthouse v6.0.

Notarás que FCP subió un poco, pero LCP cayó. Estos pequeños cambios son esencialmente “dentro del ruido” que se obtiene al ejecutar PageSpeed Insights. Todas las puntuaciones fluctúan ligeramente entre carreras.

Optimización de FCP

Con base en la puntuación anterior, sentimos que el FCP y/o el LCP podrían mejorarse aún más. Sabemos que los scripts pueden bloquear el renderizado, así que vimos cómo Next.js importa scripts en el documento:

				
					<scriptsrc="/_next/static/runtime/main-cb5fd281935517c43f30.js"async=""></script>
				
			

El uso de async aquí puede no ser la mejor opción. Si el script se descarga durante el renderizado, puede pausar el renderizado mientras se evalúa el script, lo que aumenta los tiempos FCP y LCP. El uso de defer en lugar de async garantizaría que los scripts solo se evalúen después de pintar el documento.

Desafortunadamente, Next.js no le permite cambiar la forma en que se importan los scripts, por lo que necesitábamos ampliar el componente NextScript de la siguiente manera:

				
					import React from 'react'
import { NextScript as OriginalNextScript } from 'next/document'
/**
 * A replacement for NextScript from `next/document` that gives you greater control over how script elements are rendered.
 * This should be used in the body of `pages/_document.js` in place of `NextScript`.
 */
export default class NextScript extends OriginalNextScript {
  static propTypes = {
    /**
     * Set to `defer` to use `defer` instead of `async` when rendering script elements.
     */
    mode: PropTypes.oneOf(['async', 'defer']),
  }
  static defaultProps = {
    mode: 'async',
  }
  getScripts() {
    return super.getScripts().map(script => {
      return React.cloneElement(script, {
        key: script.props.src,
        defer: this.props.mode === 'defer' ? true : undefined,
        async: this.props.mode === 'async' ? true : undefined,
      })
    })
  }
}

				
			

Luego añadimos lo siguiente a pages/_document.js:

				
					 <NextScript mode="defer" />
				
			

Para nuestro deleite, esto mejoró el LCP y las puntuaciones generales:

También golpeó ligeramente al FCP en muchas carreras, pero esto puede estar dentro del “ruido”. Sin embargo, la puntuación global fue consistentemente 2-3 puntos más alta cuando se utilizó defer vs asincrono.

Resumiendo

Cuando Lighthouse v6.0 fue lanzado a finales de mayo de 2020, las puntuaciones de rendimiento para muchos sitios, incluidas las aplicaciones React Storefront, se desplomaron. Antes de las optimizaciones, el rendimiento PDP de la aplicación de inicio React Storefront estaba sumido en la baja década de 60 Con estas optimizaciones, lo conseguimos en el aire ahora rarificado de los bajos 90s.. En este punto, creemos que la única manera de mejorar aún más la puntuación sería eliminar las dependencias, lo que puede significar el comercio de la productividad del desarrollador por el rendimiento de la aplicación.

Esa es una discusión para otro momento. Déjame dejarte con algunas cosas que probamos que no funcionaron:

Preact

Preact hace que el tamaño del paquete sea 20-30% más pequeño, pero las puntuaciones de Lighthouse v6.0 fueron consistentemente peores en todas las métricas, incluso TTI. No tenemos idea de por qué, pero sabemos que esto no es nuevo o exclusivo para Lighthouse v6.0. Era más lento con Lighthouse v5.7 también. Continuamos revisando periódicamente y esperamos que algún día esto se arregle.

Troquelado

Next.js introdujo recientemente fragmentos de grano más fino de los activos del navegador. Cuando esto se introdujo por primera vez en Next.js 9,1, notamos que los pedazos adicionales, más pequeños en realidad empeoraban el TTI. Probablemente haga que la aplicación sea más rápida para los usuarios que regresan después de que se lance una nueva versión porque puede aprovechar mejor la caché del navegador, pero Lighthouse no se preocupa por nada de eso. Así que React Storefront ha limitado el número de fragmentos del navegador a uno por un tiempo:

				
					const webpack = require('webpack')
const withReactStorefront = require('react-storefront/plugins/withReactStorefront')
module.exports = withReactStorefront({
  target: 'serverless',
  webpack: config => {
    config.plugins.push(
      new webpack.optimize.LimitChunkCountPlugin({
        maxChunks: 1,
      })
    )
    return config
  },
})

				
			

Fuentes web

La mayoría de los sitios utilizan una fuente web personalizada. De forma predeterminada, React Storefront utiliza Roboto (aunque esto se puede cambiar o eliminar). Las fuentes web personalizadas matan el rendimiento, simples y simples. Elimina la fuente web y obtendrás aproximadamente 1 segundo de FCP.

Al igual que con los análisis, las partes interesadas parecen dispuestas a cambiar el rendimiento para tener una fuente específica. React Storefront sirve la fuente web del mismo origen que el sitio para eliminar el tiempo de negociación TLS en el que incurriría si cargara la fuente desde una CDN de terceros, como Google Fonts. Específicamente, utilizamos el paquete typeface-roboto de NPM. Cuando se combina con el css-loader de Webpack , el uso de typeface-roboto da como resultado que la fuente se importe a través de un archivo CSS separado que el navegador necesita descargar y analizar. Pensamos que introducir ese CSS en el documento podría ayudar con el rendimiento, pero no lo hizo

Cuando se trata de rendimiento, siempre necesita medir. Las optimizaciones que deberían funcionar en teoría pueden no estar en la práctica.

Aumenta tu puntuación de Lighthouse

Nuestros clientes se sitúan en el percentil 95 de los 500 mejores sitios web de comercio electrónico en Lighthouse v6.0.

¡Programar una conversación consultiva!