Como melhoramos nossos principais elementos vitais da Web (estudo de caso)
Publicados: 2022-03-10No ano passado, o Google começou a enfatizar a importância dos Core Web Vitals e como eles refletem a experiência real de uma pessoa ao visitar sites na web. O desempenho é um recurso central de nossa empresa, Instant Domain Search — está no nome. Imagine nossa surpresa quando descobrimos que nossas pontuações vitais não eram boas para muitas pessoas. Nossos computadores rápidos e internet de fibra mascararam a experiência que pessoas reais têm em nosso site. Não demorou muito para que um mar de avisos vermelhos “pobres” e amarelos “precisa de melhorias” em nosso Google Search Console precisasse de nossa atenção. A entropia havia vencido, e precisávamos descobrir como resolver o problema — e tornar nosso site mais rápido.
Fundei o Instant Domain Search em 2005 e o mantive como uma atividade paralela enquanto trabalhava em uma empresa Y Combinator (Snipshot, W06), antes de trabalhar como engenheiro de software no Facebook. Recentemente, crescemos para um pequeno grupo baseado principalmente em Victoria, Canadá, e estamos trabalhando em uma longa lista de pendências de novos recursos e melhorias de desempenho. Nossas baixas pontuações de sinais vitais da web e o iminente Google Update trouxeram nosso foco para encontrar e corrigir esses problemas.
Quando a primeira versão do site foi lançada, eu a construí com PHP, MySQL e XMLHttpRequest. O Internet Explorer 6 era totalmente suportado, o Firefox estava ganhando participação e o Chrome ainda estava a anos do lançamento. Com o tempo, evoluímos por meio de uma variedade de geradores de sites estáticos, estruturas JavaScript e tecnologias de servidor. Nossa pilha de front-end atual é o React servido com Next.js e um serviço de back-end embutido Rust para responder às nossas pesquisas de nomes de domínio. Tentamos seguir as melhores práticas servindo o máximo possível em uma CDN, evitando tantos scripts de terceiros quanto possível e usando gráficos SVG simples em vez de PNGs bitmap. Não foi o suficiente.
Next.js nos permite construir nossas páginas e componentes em React e TypeScript. Quando emparelhado com o VS Code, a experiência de desenvolvimento é incrível. O Next.js geralmente funciona transformando componentes React em HTML e CSS estáticos. Dessa forma, o conteúdo inicial pode ser veiculado a partir de uma CDN e, em seguida, o Next pode “hidratar” a página para tornar os elementos dinâmicos. Depois que a página é hidratada, nosso site se transforma em um aplicativo de página única onde as pessoas podem pesquisar e gerar nomes de domínio. Não dependemos do Next.js para fazer muito trabalho do lado do servidor, a maioria do nosso conteúdo é exportado estaticamente como HTML, CSS e JavaScript para ser servido a partir de um CDN.
Quando alguém começa a pesquisar um nome de domínio, substituímos o conteúdo da página pelos resultados da pesquisa. Para tornar as pesquisas o mais rápidas possível, o front-end consulta diretamente nosso back-end Rust, que é altamente otimizado para pesquisas e sugestões de domínio. Muitas consultas podemos responder instantaneamente, mas para alguns TLDs precisamos fazer consultas DNS mais lentas, que podem levar um ou dois segundos para serem resolvidas. Quando algumas dessas consultas mais lentas forem resolvidas, atualizaremos a interface do usuário com todas as novas informações recebidas. As páginas de resultados são diferentes para todos e pode ser difícil prever exatamente como cada pessoa experimenta o site.
As Chrome DevTools são excelentes e um bom ponto de partida ao buscar problemas de desempenho. A visualização Desempenho mostra exatamente quando as solicitações HTTP são enviadas, onde o navegador gasta tempo avaliando o JavaScript e muito mais:
Existem três métricas do Core Web Vitals que o Google usará para ajudar a classificar sites em sua próxima atualização de algoritmo de pesquisa. O Google classifica as experiências em "Bom", "Precisa de melhorias" e "Ruim" com base nas pontuações de LCP, FID e CLS que pessoas reais têm no site:
- LCP , ou Largest Contentful Paint, define o tempo que leva para que o maior elemento de conteúdo fique visível.
- FID , ou First Input Delay, refere-se à capacidade de resposta de um site à interação - o tempo entre um toque, clique ou pressionamento de tecla na interface e a resposta da página.
- CLS , ou deslocamento de layout cumulativo, rastreia como os elementos se movem ou mudam na página sem ações como um teclado ou evento de clique.
O Chrome está configurado para rastrear essas métricas em todos os usuários do Chrome conectados e envia estatísticas anônimas resumindo a experiência de um cliente em um site de volta ao Google para avaliação. Essas pontuações podem ser acessadas por meio do Relatório de experiência do usuário do Chrome e são exibidas quando você inspeciona um URL com a ferramenta PageSpeed Insights. As pontuações representam a experiência do 75º percentil para as pessoas que visitaram esse URL nos 28 dias anteriores. Este é o número que eles usarão para ajudar a classificar os sites na atualização.
Uma métrica de percentil 75 (p75) atinge um equilíbrio razoável para metas de desempenho. Tirar uma média, por exemplo, esconderia muitas experiências ruins que as pessoas têm. A mediana, ou percentil 50 (p50), significaria que metade das pessoas que usaram nosso produto tiveram uma experiência pior. O percentil 95 (p95), por outro lado, é difícil de construir, pois captura muitos outliers extremos em dispositivos antigos com conexões irregulares. Achamos que a pontuação com base no percentil 75 é um padrão justo a ser alcançado.
Para controlar nossas pontuações, primeiro recorremos ao Lighthouse para obter algumas ferramentas excelentes integradas ao Chrome e hospedadas em web.dev/measure/ e no PageSpeed Insights. Essas ferramentas nos ajudaram a encontrar alguns problemas técnicos amplos em nosso site. Vimos que a maneira como o Next.js estava agrupando nosso CSS e reduzimos nosso tempo de renderização inicial, o que afetou nosso FID. A primeira vitória fácil veio de um recurso experimental do Next.js, o optimizeCss, que ajudou a melhorar significativamente nossa pontuação geral de desempenho.
O Lighthouse também detectou uma configuração incorreta de cache que impediu que alguns de nossos ativos estáticos fossem atendidos por nossa CDN. Estamos hospedados no Google Cloud Platform, e o Google Cloud CDN exige que o cabeçalho Cache-Control contenha "public". O Next.js não permite configurar todos os cabeçalhos que ele emite, então tivemos que substituí-los colocando o servidor Next.js atrás do Caddy, um servidor proxy HTTP leve implementado em Go. Também aproveitamos a oportunidade para garantir que estávamos servindo o que podíamos com o relativamente novo suporte obsoleto durante a revalidação em navegadores modernos, que permite que a CDN busque conteúdo da origem (nosso servidor Next.js) de forma assíncrona em segundo plano.
É fácil—talvez fácil demais—adicionar quase tudo que você precisa ao seu produto a partir do npm. Não demora muito para o tamanho dos pacotes crescer. Grandes pacotes demoram mais para serem baixados em redes lentas, e o celular do percentil 75 passará muito tempo bloqueando o thread principal da interface do usuário enquanto tenta entender todo o código que acabou de baixar. Gostamos do BundlePhobia, que é uma ferramenta gratuita que mostra quantas dependências e bytes um pacote npm adicionará ao seu pacote. Isso nos levou a eliminar ou substituir várias animações com mola de reação por transições CSS mais simples:
Por meio do uso do BundlePhobia e do Lighthouse, descobrimos que o registro de erros e o software de análise de terceiros contribuíram significativamente para o tamanho do pacote e o tempo de carregamento. Removemos e substituímos essas ferramentas por nosso próprio registro do lado do cliente que aproveita as APIs modernas do navegador, como sendBeacon e ping. Enviamos registros e análises para nossa própria infraestrutura do Google BigQuery, onde podemos responder às perguntas que nos interessam com mais detalhes do que qualquer uma das ferramentas disponíveis no mercado. Isso também elimina vários cookies de terceiros e nos dá muito mais controle sobre como e quando enviamos dados de log dos clientes.
Nossa pontuação CLS ainda tinha mais espaço para melhorias. A maneira como o Google calcula o CLS é complicada: você recebe uma “janela de sessão” máxima com um intervalo de 1 segundo, limitado a 5 segundos a partir do carregamento inicial da página, ou de uma interação de teclado ou clique, para terminar de mover as coisas pelo site . Se você estiver interessado em ler mais profundamente sobre este tópico, aqui está um ótimo guia sobre o assunto. Isso penaliza muitos tipos de sobreposições e pop-ups que aparecem logo após você acessar um site. Por exemplo, anúncios que mudam o conteúdo ou fazem upsell que podem aparecer quando você começa a rolar os anúncios para alcançar o conteúdo. Este artigo fornece uma excelente explicação de como a pontuação CLS é calculada e o raciocínio por trás dela.
Somos fundamentalmente contrários a esse tipo de desordem digital, por isso ficamos surpresos ao ver quanto espaço para melhorias o Google insistiu que fizéssemos. O Chrome possui uma sobreposição de Web Vitals integrada que você pode acessar usando o menu de comando para "Mostrar sobreposição de principais Web Vitals". Para ver exatamente quais elementos o Chrome considera em seu cálculo de CLS, achamos a opção “Console Logging” da extensão Chrome Web Vitals nas configurações mais útil. Uma vez ativado, este plug-in mostra suas pontuações LCP, FID e CLS para a página atual. No console, você pode ver exatamente quais elementos da página estão conectados a essas pontuações. Nossas pontuações CLS tiveram mais espaço para melhorias.
Das três métricas, o CLS é o único que se acumula à medida que você interage com uma página. A extensão Web Vitals tem uma opção de registro que mostrará exatamente quais elementos causam CLS enquanto você está interagindo com um produto. Veja como as métricas do CLS são adicionadas quando rolamos a página inicial da Smashing Magazine:
O Google continuará ajustando a forma como calcula o CLS ao longo do tempo, por isso é importante manter-se informado seguindo o blog de desenvolvimento web do Google. Ao usar ferramentas como a extensão Chrome Web Vitals, é importante ativar a limitação da CPU e da rede para obter uma experiência mais realista. Você pode fazer isso com as ferramentas do desenvolvedor simulando uma CPU móvel.
A melhor maneira de acompanhar o progresso de uma implantação para a próxima é medir as experiências de página da mesma forma que o Google faz. Se você configurou o Google Analytics, uma maneira fácil de fazer isso é instalar o módulo web-vitals do Google e conectá-lo ao Google Analytics. Isso fornece uma medida aproximada do seu progresso e o torna visível em um painel do Google Analytics.
Este é o lugar onde batemos em uma parede. Pudemos ver nossa pontuação no CLS e, embora a tenhamos melhorado significativamente, ainda tínhamos trabalho a fazer. Nossa pontuação no CLS foi de aproximadamente 0,23 e precisávamos ficar abaixo de 0,1 – e de preferência até 0. Nesse ponto, porém, não conseguimos encontrar algo que nos dissesse exatamente quais componentes em quais páginas ainda estavam afetando a pontuação. Pudemos ver que o Chrome expôs muitos detalhes em suas ferramentas Core Web Vitals, mas que os agregadores de log jogaram fora a parte mais importante: exatamente qual elemento da página causou o problema.
Para capturar todos os detalhes de que precisamos, criamos uma função sem servidor para capturar dados vitais da web dos navegadores. Como não precisamos executar consultas em tempo real nos dados, nós os transmitimos para a API de streaming do Google BigQuery para armazenamento. Essa arquitetura significa que podemos capturar de forma barata tantos pontos de dados quantos pudermos gerar.
Depois de aprender algumas lições enquanto trabalhamos com o Web Vitals e o BigQuery, decidimos agrupar essa funcionalidade e lançar essas ferramentas como código aberto em vitals.dev.
O uso do Instant Vitals é uma maneira rápida de começar a rastrear suas pontuações do Web Vitals no BigQuery. Veja um exemplo de esquema de tabela do BigQuery que criamos:
A integração com o Instant Vitals é fácil. Você pode começar integrando-se à biblioteca cliente para enviar dados para seu back-end ou função sem servidor:
import { init } from "@instantdomain/vitals-client"; init({ endpoint: "/api/web-vitals" });
Então, no seu servidor, você pode integrar com a biblioteca do servidor para completar o circuito:
import fs from "fs"; import { init, streamVitals } from "@instantdomain/vitals-server"; // Google libraries require service key as path to file const GOOGLE_SERVICE_KEY = process.env.GOOGLE_SERVICE_KEY; process.env.GOOGLE_APPLICATION_CREDENTIALS = "/tmp/goog_creds"; fs.writeFileSync( process.env.GOOGLE_APPLICATION_CREDENTIALS, GOOGLE_SERVICE_KEY ); const DATASET_; init({ datasetId: DATASET_ID }).then().catch(console.error); // Request handler export default async (req, res) => { const body = JSON.parse(req.body); await streamVitals(body, body.name); res.status(200).end(); };
Basta chamar streamVitals
com o corpo da solicitação e o nome da métrica para enviar a métrica ao BigQuery. A biblioteca cuidará da criação do conjunto de dados e das tabelas para você.
Depois de coletar os dados de um dia, executamos esta consulta como esta:
SELECT `<project_name>.web_vitals.CLS`.Value, Node FROM `<project_name>.web_vitals.CLS` JOIN UNNEST(Entries) AS Entry JOIN UNNEST(Entry.Sources) WHERE Node != "" ORDER BY value LIMIT 10
Esta consulta produz resultados como este:
Valor | Nó |
---|---|
4.6045324800736724E-4 | /html/body/div[1]/main/div/div/div[2]/div/div/blockquote |
7.183070668914928E-4 | /html/body/div[1]/header/div/div/header/div |
0.031002668277977697 | /html/body/div[1]/footer |
0.035830703317463526 | /html/body/div[1]/main/div/div/div[2] |
0.035830703317463526 | /html/body/div[1]/footer |
0.035830703317463526 | /html/body/div[1]/main/div/div/div[2] |
0.035830703317463526 | /html/body/div[1]/main/div/div/div[2] |
0.035830703317463526 | /html/body/div[1]/footer |
0.035830703317463526 | /html/body/div[1]/footer |
0.03988482067913317 | /html/body/div[1]/footer |
Isso nos mostra quais elementos em quais páginas têm mais impacto no CLS. Ele criou uma lista de pendências para nossa equipe investigar e corrigir. Na Pesquisa de domínio instantânea, verifica-se que conexões móveis lentas ou ruins levarão mais de 500 ms para carregar alguns de nossos resultados de pesquisa. Um dos piores contribuintes do CLS para esses usuários foi, na verdade, nosso rodapé.
A pontuação de mudança de layout é calculada em função do tamanho do elemento em movimento e da distância que ele percorre. Em nossa visualização de resultados de pesquisa, se um dispositivo demorar mais do que um determinado período de tempo para receber e renderizar os resultados da pesquisa, a visualização de resultados será recolhida para uma zero-height
, trazendo o rodapé à vista. Quando os resultados chegam, eles empurram o rodapé de volta para a parte inferior da página. Um grande elemento DOM chegando até aqui adicionou muito à nossa pontuação no CLS. Para trabalhar com isso corretamente, precisamos reestruturar a maneira como os resultados da pesquisa são coletados e renderizados. Decidimos apenas remover o rodapé na visualização dos resultados da pesquisa como um hack rápido que impediria que ele pulasse em conexões lentas.
Agora revisamos este relatório regularmente para acompanhar como estamos melhorando - e o usamos para combater os resultados em declínio à medida que avançamos. Testemunhamos o valor da atenção extra aos recursos e produtos recém-lançados em nosso site e operacionalizamos verificações consistentes para garantir que os principais pontos vitais estejam agindo em favor de nossa classificação. Esperamos que, ao compartilhar Instant Vitals, possamos ajudar outros desenvolvedores a lidar com suas pontuações de Core Web Vitals também.
O Google oferece excelentes ferramentas de desempenho integradas ao Chrome, e as usamos para encontrar e corrigir vários problemas de desempenho. Aprendemos que os dados de campo fornecidos pelo Google ofereciam um bom resumo do progresso do nosso p75, mas não tinham detalhes acionáveis. Precisávamos descobrir exatamente quais elementos DOM estavam causando mudanças de layout e atrasos de entrada. Assim que começamos a coletar nossos próprios dados de campo — com consultas XPath — conseguimos identificar oportunidades específicas para melhorar a experiência de todos em nosso site. Com algum esforço, reduzimos nossas pontuações de campo do Core Web Vitals do mundo real para um intervalo aceitável em preparação para a Atualização de experiência de página de junho. Estamos felizes em ver esses números caindo e para a direita!