Home Blogs Otimizando seu site para Lighthouse v6.0
Applications

Otimizando seu site para Lighthouse v6.0

About The Author

Outline

Layer0 é um dos principais contribuintes para o framework PWA de comércio eletrônico de código aberto, React Storefront. No início deste ano, contribuímos com muitos novos recursos e otimizações como React Storefront v7, Duas das mais significativas foram a mudança para Next.js e a remoção de várias dependências de chave (por exemplo, MobX) em favor dos novos recursos internos do React para gerenciar o estado, como o hook useState e a API de contexto. Isso resultou no tamanho do pacote do navegador sendo cortado aproximadamente pela metade.

Este foi um bom ganho na época e ajudou a melhorar as pontuações de desempenho do Lighthouse (v5.7) para aplicativos típicos do React Storefront dos anos 80 até os anos 90, conforme medido pelo PageSpeed Insights (PSI). Por contexto, uma pontuação de mais de 83 superou 99% dos 500 melhores sites de comércio eletrônico do Lighthouse v5.7. Não percebemos o quão essencial seria a redução do pacote nos próximos meses, quando o Lighthouse v6.0 cairia como uma bomba e obliteraria as pontuações de desempenho de todos.

Veja como a distribuição das pontuações do Lighthouse mensuradas em PSI para os principais sites de comércio eletrônico mudou quando a v6.0 caiu:

Neste post, compartilhamos como melhoramos as pontuações do Lighthouse v6.0 para React Storefront, mas as técnicas podem ser aplicadas a outros frameworks.

Além disso, é importante notar que o Google anunciou em 28 de maio de 2020 as métricas específicas que eles usarão para classificar sites em 2021. A pontuação de desempenho do Lighthouse não será usada, embora alguns elementos usados para determinar essa pontuação, e mesmo assim, eles não serão medidos usando um teste sintético como o Lighthouse, mas sim dados de campo do mundo real do Chrome User Experience Report (crux).

As novas métricas do Lighthouse 6,0: TBT, LCP e CLS

O Lighthouse v6.0 apresenta várias novas métricas de velocidade percetiva e reformula como as métricas gerais afetam a pontuação de desempenho do Lighthouse de uma página.

Farol 5,7 Peso Farol 6,0 Peso
Primeira pintura Contentful (FCP) 20% Primeira pintura Contentful (FCP) 15%
Índice de velocidade (SI) 27% Índice de velocidade (SI) 15%
Primeira pintura significativa (FMP) 7% Maior Contenful Paint (LCP) 25%
Primeira CPU ociosa (FCI) 13% Tempo total de bloqueio (TBT) 15%
Tempo para Interativo (TTI) 33% Tempo para Interativo (TTI) 15%
- - Mudança de esquema cumulativa (CLS) 5%

Tempo total de bloqueio (TBT)

O tempo total de bloqueio é uma nova métrica incluída no Lighthouse v6.0 que mede o tempo que a análise e a execução do JavaScript bloqueia o thread principal durante o carregamento da página. Esta métrica trata spas/PWAS modernos e pesados em JavaScript muito duramente. Mesmo com o corte de 50% do React Storefront 7 no tamanho do pacote, vimos um mergulho de 20 a 30 pontos na pontuação de desempenho do Lighthouse v6.0 para páginas de produtos, em grande parte devido à inclusão do TBT como uma métrica que influencia 25% da pontuação geral de desempenho.

Se você estiver usando uma estrutura isomórfica, como Next.js, que suporta renderização do lado do servidor, o TBT é determinado principalmente pelo tamanho do pacote e pelo tempo de hidratação. Simplificando, a única maneira de melhorar o TBT é remover dependências, otimizar seus componentes ou tornar seu site mais simples usando menos componentes.

Maior Contentful Paint (LCP)

LCP é uma nova métrica que tem um peso de 25% sobre a pontuação geral do Lighthouse v6.0. O LCP tem como objetivo quantificar como o usuário percebe o desempenho inicial do carregamento da página observando quanto tempo o maior elemento contento leva para terminar a pintura. Para a maioria dos sites, especialmente em sites de comércio eletrônico, o maior elemento contento é a imagem do herói. No caso de páginas de produtos nos aplicativos React Storefront, o maior elemento contento é a imagem principal do produto. Se você não tem certeza de qual elemento isso está em seu site, PSI lhe dirá:

Para otimizar o LCP, tem de garantir que a imagem é carregada o mais rapidamente possível.

Mudança de esquema cumulativa (CLS)

A mudança cumulativa de layout mede o quanto o layout da página muda durante o carregamento inicial da página. A mudança de layout é mais comumente causada por imagens, que tendem a empurrar os elementos em torno deles à medida que eles são redimensionados para acomodar a imagem depois que os dados são baixados da rede. A mudança de layout pode ser totalmente eliminada, reservando espaço para cada imagem antes de carregar. Felizmente, o componente Image do React Storefront já faz isso, então o aplicativo inicial do React Storefront possui uma pontuação CLS perfeita de 0 para fora da caixa.

Deve-se notar que outros culpados comuns de CLS pobres são banners e pop-ups que aparecem depois que a página é inicialmente pintada. Os usuários os odeiam e agora o Lighthouse também.

Como otimizamos a React Storefront para o Lighthouse v6.0

Quando testamos pela primeira vez a página de produto do aplicativo inicial React Storefront no Lighthouse v6.0, usando o PageSpeed Insights, ele marcou nos anos 60:

Para melhorar a pontuação, primeiro definimos nosso ponto de vista sobre o LCP. Aos 2,5 segundos, o FCP estava bastante alto (chegaremos a isso mais tarde), mas a diferença de quase 3 segundos entre o FCP e o LCP se destacou como algo que precisava de melhorias.

Otimização do LCP

A imagem principal do produto React Storefront (meramente um espaço reservado – a caixa verde com “Produto 1” no centro) foi renderizada como uma tag img HTML com um URL src que aponta para https://via.placeholder.com. Esse site tem um TTFB decente (cerca de 150ms), que poderia ser melhorado movendo-se para uma CDN mais rápida como Layer0 CDN-as-JavaScript. Ainda assim, duvidamos que 150 ms representavam a diferença de quase 3 segundos entre o FCP e o LCP. Para eliminar a lacuna, nós inforramos a imagem usando um URL de dados base64 como este:

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

				
			

Fizemos isso baixando a imagem principal, convertendo-a para base64 e enchendo-a no atributo src durante a renderização do lado do servidor na nuvem JavaScript sem servidor do Layer0. Aqui está a linha no endpoint da API do produto:

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

E aqui está o código que busca e converte a imagem para 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')}`
}

				
			

Muito simples e velha escola. E o impactos na pontuação?

Ao deixar cair o LCP de 5.3s para 2.8s, ganhamos 21 pontos na pontuação Lighthouse v6.0 da página! É um pouco perturbador como uma mudança tão pequena pode afetar drasticamente a pontuação do Lighthouse v6.0, mas vamos levá-la. Deve-se notar que todas as métricas variam um pouco entre corridas, mas a pontuação geral foi consistente na baixa década de 80. Para o contexto, o site líder de eCommerce de maior desempenho na v6.0 obteve 87 pontos, medido em PSI e parece que está direto dos anos 90 – dê uma olhada em www.rockauto.com

A diferença entre o FCP e o LCP mostrado acima era tão grande quanto vimos em várias corridas. Na maioria das vezes, a diferença estava na faixa de 100ms a 300ms. Ocasionalmente FCP e LCP eram os mesmos.

Otimização do TBT

Em seguida, tentamos melhorar o TBT. Isso foi bastante desafiador. Como mencionado anteriormente, para melhorar o LCP, você precisa reduzir o tamanho do seu pacote JavaScript ou melhorar o tempo de hidratação. Com a maioria dos aplicativos, simplesmente não é viável começar a extrair dependências para tornar o pacote menor. Os aplicativos criados com o React Storefront 7 já se beneficiam de muitas otimizações em tempo de compilação que minimizam o tamanho do pacote fornecido pela configuração do webpack do Next.js, bem como otimizações específicas do babel para a UI do material. Então, onde poderíamos melhorar? Tempo de hidratação.

Hidratação preguiçosa para a vitória!

Felizmente, a comunidade React Storefront já havia começado a trabalhar para apoiar a hidratação preguiçosa antes do Lighthouse v6.0 cair. Isso certamente nos fez acelerar nossos esforços.

No caso de você não ter conhecimento, Hidratação refere-se a React assumindo o controle de elementos HTML renderizados no servidor para que eles possam se tornar interativos. Os botões tornam-se clicáveis; carrosséis tornam-se swipeable, etc. Quanto mais componentes uma página tiver, mais tempo demora a hidratação. Componentes complexos, como o menu principal e o carrossel de imagens do produto, demoram ainda mais.

A hidratação preguiçosa implica atrasar a hidratação de certos componentes até que seja absolutamente necessária e, o mais importante, após a carga inicial da página (e após o TBT ser calculado). A hidratação preguiçosa pode ser arriscada. Você deve garantir que os elementos da página estejam prontos para responder à entrada do usuário antes que o usuário tente interagir com eles.

Implementar a hidratação preguiçosa no React Storefront provou ser bastante difícil devido à dependência do material da UI em CSS-in-JS, que dinamicamente adiciona estilos ao documento somente depois que os componentes são hidratados. Vou guardar os detalhes para outra vez. No final, construímos um componente LazyHydrate que os desenvolvedores podem inserir em qualquer lugar da árvore de componentes para atrasar a hidratação até que um evento específico ocorra, como o elemento que está sendo rolado na janela de visualização ou o usuário tocando na tela.

Aqui está um exemplo em que nós hidratamos preguiçosamente o MediaCarousel que exibe as principais imagens do produto:

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

				
			

Aplicamos a hidratação preguiçosa em várias áreas da aplicação, mais notavelmente:

  • O menu deslizante: Hidratamos isso quando o usuário toca no botão hambúrguer.
  • Todos os controles abaixo da dobra: Incluem os seletores de tamanho, cor e quantidade, bem como guias de informações do produto.
  • O carrossel da imagem principal: Este e o menu principal são provavelmente os componentes com mais funcionalidade e, portanto, os mais caros de hidratar.

Aqui está a pontuação Lighthouse v6.0 com hidratação preguiçosa aplicada:

A hidratação preguiçosa cortou TBT em quase 40% e o TTI aparado (que tem um peso de 15% sobre as pontuações na v6.0) em 700ms. Isso marcou um ganho de 6 pontos na pontuação geral do Lighthouse v6.0.

Você notará que o FCP subiu um pouco, mas o LCP desceu. Essas pequenas alterações são essencialmente “dentro do ruído” que você obtém ao executar o PageSpeed Insights. Todas as pontuações flutuam ligeiramente entre corridas.

Otimização do FCP

Com base na pontuação acima, sentimos que o FCP e/ou o LCP poderiam ser melhorados ainda mais. Sabemos que os scripts podem bloquear a renderização, então analisamos como o Next.js importa scripts para o documento:

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

Usar async aqui pode não ser a melhor escolha. Se o script for baixado durante a renderização, ele poderá pausar a renderização enquanto o script for avaliado, o que aumenta os tempos FCP e LCP. O uso de defer em vez de async garantiria que os scripts só sejam avaliados após o documento ser pintado.

Infelizmente, o Next.js não permite que você altere a forma como os scripts são importados, então precisávamos estender o componente NextScript da seguinte forma:

				
					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,
      })
    })
  }
}

				
			

Em seguida, adicionamos o seguinte ao Pages/_document.js:

				
					 <NextScript mode="defer" />
				
			

Para o nosso prazer, isso melhorou o LCP e as pontuações gerais:

Ele também bateu ligeiramente o FCP em muitas corridas, mas isso pode estar dentro do “ruído”. No entanto, a pontuação geral foi consistentemente 2-3 pontos maior quando se utiliza o defer vs async.

Resumindo

Quando o Lighthouse v6.0 foi lançado no final de maio de 2020, as pontuações de desempenho de muitos sites, incluindo os aplicativos React Storefront, caíram. Antes das otimizações, o desempenho do PDP do aplicativo inicial do React Storefront foi reduzido nos anos 60. Com estas otimizações, entramos no ar agora raarizado da baixa década de 90. Neste ponto, achamos que a única maneira de melhorar ainda mais a pontuação seria remover dependências, o que pode significar a negociação da produtividade do desenvolvedor para o desempenho do aplicativo.

Essa é uma discussão para outra época. Deixe-me deixá-lo com algumas coisas que tentamos que não funcionaram:

Preact

O Preact torna o tamanho do pacote 20-30% menor, mas as pontuações do Lighthouse v6.0 foram consistentemente piores em todas as métricas, até mesmo TTI. Não fazemos ideia do porquê, mas sabemos que isso não é novo ou exclusivo para o Lighthouse v6.0. Foi mais lento com Lighthouse v5.7 também. Continuamos a fazer check-in periodicamente e esperamos que algum dia isso seja corrigido.

Chunking

O Next.js introduziu recentemente um agrupamento mais refinado de ativos do navegador. Quando isso foi introduzido pela primeira vez no Next.js 9,1, percebemos que os pedaços menores adicionais realmente pioraram o TTI. Ele provavelmente torna o aplicativo mais rápido para os usuários que retornam depois que uma nova versão é lançada porque pode aproveitar melhor o cache do navegador, mas o Lighthouse não se importa com nada disso. Assim, o React Storefront limitou o número de chunks do navegador a um por um tempo:

				
					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
  },
})

				
			

Fontes da Web

A maioria dos sites usa uma fonte web personalizada. Por padrão, o React Storefront usa o Roboto (embora isso possa ser alterado ou removido). Fontes web personalizadas eliminam o desempenho, simples e simples. Remova a fonte web e você ganhará cerca de 1 segundo de FCP.

Tal como acontece com a análise, as partes interessadas parecem dispostas a negociar o desempenho para ter uma fonte específica. O React Storefront serve a fonte da Web da mesma origem do site para eliminar o tempo de negociação TLS que você incorrerá se você carregou a fonte de um CDN de terceiros, como o Google Fonts. Especificamente, usamos o pacote typeface-roboto da NPM. Quando combinado com o css-loader do Webpack , o uso de typeface-roboto resulta na importação da fonte através de um arquivo CSS separado que o navegador precisa baixar e analisar. Nós pensamos que inlining esse CSS no documento pode ajudar com o desempenho, mas não.

Quando se trata de desempenho, você sempre precisa medir. Otimizações que devem funcionar em teoria podem não estar na prática.

Aumente a sua pontuação no Farol

Nossos clientes estão no percentil 95 dos 500 principais sites de comércio eletrônico do Lighthouse v6.0.

Agende uma conversa consultiva!