Home Blogs Otimizando o seu site para o Lighthouse v6.0
Applications

Otimizando o seu site para o 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 das novas capacidades integradas do React para gerir o estado, como o hook useState e a API de contexto. Isto resultou no tamanho do pacote do navegador a ser cortado cerca de metade.

Este foi um bom ganho na altura e ajudou a melhorar as pontuações de desempenho do Lighthouse (v5.7) para aplicações típicas do React Storefront dos anos 80 aos 90, conforme medido pelo PageSpeed Insights (ISP). Por contexto, uma pontuação de mais de 83 superou 99% dos 500 melhores sites de comércio eletrônico no 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 largava como uma bomba e obliterava as pontuações de desempenho de todos.

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

Neste post, nós 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 farol não será usada, embora alguns elementos usados para determinar essa pontuação não serão medidos usando um teste sintético como o farol, mas sim dados de campo do mundo real do relatório de experiência do usuário do Chrome (crux).

As novas métricas do farol 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 Interactivo (TTI) 33% Tempo para Interactivo (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 tópico principal durante o carregamento da página. Esta métrica trata os modernos e pesados spas/DWAs com JavaScript muito rigor. 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 um framework isomórfico, como o Next.js, que suporta renderização no 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.

Pintura com maior conteúdo (LCP)

O LCP é uma nova métrica que tem um peso de 25% sobre a pontuação geral do Lighthouse v6.0. O LCP pretende quantificar como o utilizador percebe o desempenho inicial do carregamento da página observando quanto tempo demora o maior elemento contento 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 das páginas de produtos nos aplicativos React Storefront, o maior elemento contento é a imagem principal do produto. Se não tiver a certeza de qual é o elemento que está no seu site, a PSI irá dizer-lhe:

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 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 à sua volta à medida que eles se redimensionam 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, por isso a aplicação inicial do React Storefront tem uma pontuação CLS perfeita de 0. Deve-se notar que outros culpados comuns dos CLS pobres são banners e pop-ups que aparecem depois que a página é inicialmente pintada. Os utilizadores odeiam-nos e agora o farol também o faz.

Como otimizámos a React Storefront para o Lighthouse v6.0

Quando testámos pela primeira vez a página de produto da aplicação React Storefront starter no Lighthouse v6.0, usando o PageSpeed Insights, obteve uma pontuação nos anos 60:

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

Optimizar o LCP

A imagem principal do React Storefront (apenas um marcador de posição – a caixa verde com o “Produto 1” no centro) foi renderizada como uma tag html img com um URL src que aponta para https://via.placeholder.com. Esse site tem um TTFB decente (cerca de 150 ms), 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 lacuna 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 a 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 o LCP cair de 5,5 para 2,8 segundos, ganhámos 21 pontos na pontuação do Lighthouse v6.0 da página! É um pouco inquietante 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 as corridas, mas a pontuação geral foi consistente nos anos 80 baixos. Por contexto, o site líder de eCommerce com maior desempenho na v6.0 obteve 87 pontos medidos em PSI e parece que está logo nos anos 90. Vejam www.rockauto.com

O fosso entre o FCP e o LCP acima mostrado era quase tão grande como o vimos em várias corridas. Na maioria das vezes, o intervalo estava entre 100 ms e 300 ms. Ocasionalmente o FCP e o LCP eram os mesmos.

Optimizar o TBT

Em seguida, tentámos melhorar o TBT. Isto foi bastante desafiador. Como mencionado anteriormente, para melhorar o LCP, é necessário reduzir o tamanho do seu pacote JavaScript ou melhorar o tempo de hidratação. Com a maioria dos aplicativos, simplesmente não é possí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 interface do usuário 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á tinha começado a trabalhar para apoiar a hidratação preguiçosa antes de o Lighthouse v6.0 cair. Isso certamente nos fez acelerar os nossos esforços.

No caso de não saberem, Hydration refere-se a React assumir o controlo dos elementos HTML renderizados no servidor para que possam tornar-se interativos. Os botões tornam-se clicáveis; os 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 depois de o TBT ser calculado). A hidratação preguiçosa pode ser arriscada. É preciso 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 interface do usuário no CSS-in-JS, que dinamicamente adiciona estilos ao documento apenas 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 na árvore de componentes para atrasar a hidratação até que ocorra um evento específico, como o elemento que está a ser rolado na janela de visualização ou o utilizador a tocar no ecrã.

Aqui está um exemplo em que 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 slide-in: Hidratamos isto quando o utilizador toca no botão do hambúrguer.
  • Todos os controles abaixo da dobra: Incluem o tamanho, cor e quantidade seletores, bem como guias de informações do produto.
  • O carrossel da imagem principal: Este e o menu principal são provavelmente os componentes com maior funcionalidade e, portanto, os mais caros de hidratar.

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

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

Vão notar que o FCP subiu um pouco, mas o LCP desceu. Estas pequenas alterações estão essencialmente “dentro do ruído” que se obtém ao executar o PageSpeed Insights. Todas as pontuações flutuam ligeiramente entre corridas.

Optimizar o 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 pode pausar a renderização enquanto o script é avaliado, o que aumenta os tempos de FCP e LCP. Usar o defer em vez do async garantiria que os scripts só fossem avaliados depois que o documento fosse pintado.

Infelizmente, o Next.js não permite que você altere a forma como os scripts são importados, por isso 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 page/_document.js:

				
					 <NextScript mode="defer" />
				
			

Para o nosso prazer, isto 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 mais alta quando se usa 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 da aplicação React Storefront starter estava atolado nos anos 60. Com estas otimizações, conseguimos entrar no ar agora raarizado dos anos 90 baixos. Neste ponto, pensamos que a única maneira de melhorar ainda mais a pontuação seria remover dependências, o que pode significar a troca da produtividade do desenvolvedor para o desempenho do aplicativo.

Essa é uma discussão para outra época. Deixem-me deixar-vos com algumas coisas que tentámos que não funcionaram:

Preacto

O Preacto 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 no TTI. Não fazemos ideia do porquê, mas sabemos que isto não é novo ou exclusivo para o Lighthouse v6.0. Foi mais lento com o farol v5.7 também. Continuamos a fazer o check-in periodicamente e esperamos que um dia isto seja resolvido.

Chunking

O Next.js introduziu recentemente um agrupamento mais refinado de ativos de navegador. Quando isto foi introduzido pela primeira vez no Next.js 9,1, reparámos que os pedaços mais pequenos tornaram o TTI pior. 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. Então o React Storefront limitou o número de blocos de 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 matam 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 trocar o desempenho para terem uma fonte específica. O React Storefront serve a fonte da Web da mesma origem do site para eliminar o tempo de negociação de TLS que incorreria se carregasse a fonte de uma 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 do tipo de letra-roboto resulta na importação da fonte através de um arquivo CSS separado que o navegador precisa baixar e analisar. Pensámos que inlining esse CSS no documento pode ajudar com o desempenho, mas não o fez.

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 melhores sites de comércio eletrônico do Lighthouse v6.0.

Agende uma conversa consultiva!