Como e por que criamos insights de performance

No Chrome 102, você vai notar um novo painel experimental, Performance Insights, nas Ferramentas para desenvolvedores. Neste post, vamos discutir não apenas por que estamos trabalhando em um novo painel, mas também os desafios técnicos que enfrentamos e as decisões que tomamos ao longo do caminho.

ALT_TEXT_HERE

Por que criar outro painel?

Se você ainda não viu, postamos um vídeo sobre por que criar o painel "Insights de performance" e como você pode receber insights úteis sobre a performance do seu site com ele.

O Painel de performance atual é um ótimo recurso se você quiser conferir todos os dados do seu site em um só lugar, mas achamos que ele pode ser um pouco confuso. Se você não for um especialista em performance, será difícil saber exatamente o que procurar e quais partes da gravação são relevantes.

Acesse o painel de insights, onde você ainda pode conferir uma linha do tempo do seu rastreamento e inspecionar os dados, além de acessar uma lista prática do que as Ferramentas do desenvolvedor consideram ser os principais "insights" que valem a pena analisar. Os insights vão identificar problemas como solicitações de bloqueio de renderização, mudanças de layout e tarefas longas, entre outros, que podem afetar negativamente a performance de carregamento de página do site e, especificamente, as pontuações de Core Web Vital (CWV) do site. Além de sinalizar problemas, os insights de performance oferecem sugestões úteis para melhorar suas pontuações de CWV e links para outros recursos e documentação.

Link de feedback no painel

Este painel é experimental, e queremos saber sua opinião. Informe se você encontrar bugs ou tiver solicitações de recursos que podem ajudar na performance do seu site.

Como criamos os Insights de performance

Assim como o restante do DevTools, criamos Insights de desempenho no TypeScript e usamos componentes da Web, com suporte de lit-html, para criar a interface do usuário. A diferença entre os Insights de desempenho é que a interface principal da interface é um elemento HTML canvas, e a linha do tempo é desenhada nessa tela. Grande parte da complexidade vem do gerenciamento dessa tela: não apenas desenhar os detalhes certos no lugar certo, mas também gerenciar eventos do mouse (por exemplo, onde o usuário clicou na tela? Eles clicaram em um evento que desenhamos?) e garantiu que renderizemos novamente a tela com eficiência.

Várias faixas em uma única tela

Para um determinado site, há várias "faixas" que queremos renderizar, cada uma representando uma categoria diferente de dados. Por exemplo, o painel "Insights" mostra três faixas por padrão:

À medida que continuamos a lançar recursos no painel, esperamos adicionar mais faixas.

Nossa ideia inicial era que cada uma dessas faixas renderizasse o próprio <canvas>, para que a visualização principal se tornasse vários elementos de tela empilhados verticalmente. Isso simplificaria a renderização no nível da faixa, porque cada faixa poderia ser renderizada de forma isolada e não haveria perigo de uma faixa ser renderizada fora dos limites dela, mas essa abordagem tem dois problemas principais:

Os elementos canvas são caros para (re)renderizar. Ter várias telas é mais caro do que uma única tela, mesmo que ela seja maior. A renderização de sobreposições que passam por várias faixas (por exemplo, linhas verticais para marcar eventos como o tempo FCP) se torna complexa: precisamos renderizar em várias telas e garantir que todas sejam renderizadas juntas e alinhadas corretamente.

O uso de um canvas para toda a interface exigiu que descobrimos como garantir que cada faixa seja renderizada nas coordenadas corretas e não transborde para outra faixa. Por exemplo, se uma faixa específica tem 100 px de altura, não podemos permitir que ela renderize algo com 120 px de altura e vague a faixa que está abaixo dela. Para resolver isso, podemos usar clip. Antes de renderizar cada faixa, desenhamos um retângulo que representa a janela visível da faixa. Isso garante que todos os caminhos desenhados fora desses limites sejam cortados pela tela.

canvasContext.beginPath();
canvasContext.rect(
    trackVisibleWindow.x, trackVisibleWindow.y, trackVisibleWindow.width, trackVisibleWindow.height);
canvasContext.clip();

Também não queríamos que cada faixa precisasse saber a posição vertical: cada faixa precisa ser renderizada como se estivesse sendo renderizada em (0, 0), e temos um componente de nível mais alto (chamado de TrackManager) para gerenciar a posição geral da faixa. Isso pode ser feito com translate, que traduz a tela para uma posição (x, y) específica. Exemplo:

canvasContext.translate(0, 10); // Translate by 10px in the y direction
canvasContext.rect(0, 0, 10, 10); // draw a rectangle at (0, 0) that’s 10px high and wide

Apesar de o código rect definir 0, 0 como a posição, a tradução geral aplicada fará com que o retângulo seja renderizado em 0, 10. Isso nos permite trabalhar em uma faixa como se estivéssemos renderizando em (0, 0) e fazer com que o gerenciador de faixas traduza conforme renderiza cada faixa para garantir que cada uma seja renderizada corretamente abaixo da anterior.

Telas fora da tela para faixas e destaques

A renderização de tela é relativamente cara, e queremos garantir que o painel de insights continue suave e responsivo durante o trabalho. Às vezes, não é possível evitar ter que renderizar toda a tela novamente, por exemplo, se você mudar o nível de zoom, teremos que começar de novo e renderizar tudo de novo. A renderização de tela é particularmente custosa porque não é possível renderizar apenas uma pequena parte dela. É necessário limpar a tela inteira e renderizar novamente. Isso é diferente da renderização do DOM, em que as ferramentas podem calcular o trabalho mínimo necessário e não remover tudo e começar de novo.

Um dos problemas visuais que encontramos foi o destaque. Quando você passa o mouse sobre as métricas no painel, nós as destacamos na linha do tempo. Da mesma forma, se você passar o cursor sobre um insight de um determinado evento, desenhamos uma borda azul ao redor desse evento.

Esse recurso foi implementado pela primeira vez detectando um movimento do mouse sobre um elemento que aciona um destaque e, em seguida, desenhando esse destaque diretamente na tela principal. O problema ocorre quando precisamos remover o destaque: a única opção é redesenhar tudo. Não é possível redesenhar a área em que o destaque estava (sem grandes mudanças na arquitetura), mas redesenhar toda a tela apenas porque queremos remover uma borda azul ao redor de um item parecia exagerado. Ele também tinha um atraso visual se você movesse o mouse rapidamente sobre itens diferentes para acionar vários destaques em rápida sucessão.

Para corrigir isso, dividimos nossa interface em duas telas fora da tela: a principal, onde as faixas são renderizadas, e a tela "destaque", em que os destaques são desenhados. Em seguida, renderizamos copiando essas telas para a única tela visível na tela para o usuário. Podemos usar o método drawImage em um contexto de tela, que pode usar outra tela como fonte.

Isso significa que a remoção de um destaque não faz com que a tela principal seja redesenhada. Em vez disso, podemos limpar a tela na tela e copiar a tela principal para a tela visível. O ato de copiar uma tela é barato, é o desenho que é caro; por isso, ao mover os destaques para uma tela separada, evitamos esse custo ao ativar e desativar realces.

Análise de rastros amplamente testada

Um dos benefícios de criar um novo recurso do zero é que você pode refletir sobre as escolhas técnicas feitas anteriormente e fazer melhorias. Uma das coisas que queríamos melhorar era dividir explicitamente nosso código em duas partes quase totalmente distintas:

Analise o arquivo de rastreamento e extraia os dados necessários. Renderizar um conjunto de faixas.

Manter a análise (parte 1) separada do trabalho da interface (parte 2) nos permitiu criar um sistema de análise sólido. Cada rastro é executado por uma série de gerenciadores responsáveis por diferentes problemas: um LayoutShiftHandler calcula todas as informações necessárias para os deslocamentos de layout, e um NetworkRequestsHandler lida exclusivamente com a extração de solicitações de rede. Ter essa etapa de análise explícita em que temos gerenciadores diferentes responsáveis por diferentes partes do rastro também foi benéfico: a análise de rastros pode ficar muito complicada, e é útil se concentrar em uma preocupação por vez.

Também foi possível testar de forma abrangente nossa análise de rastros fazendo gravações no DevTools, salvando-as e carregando-as como parte do nosso conjunto de testes. Isso é ótimo porque podemos testar com rastros reais, e não acumular grandes quantidades de dados de rastro falsos que podem se tornar obsoletos.

Teste de captura de tela para a interface da tela

No que diz respeito aos testes, geralmente renderizamos os componentes do front-end no navegador e garantimos que eles se comportem como esperado. Podemos enviar eventos de clique para acionar atualizações e garantir que o DOM gerado pelos componentes esteja correto. Essa abordagem funciona bem para nós, mas falha quando consideramos a renderização em uma tela. Não há como inspecionar uma tela e determinar o que está sendo desenhado nela. Portanto, nossa abordagem habitual de renderização e consulta não é apropriada.

Para ter uma cobertura de teste, usamos testes de captura de tela. Cada teste ativa uma tela, renderiza a faixa que queremos testar e faz uma captura de tela do elemento da tela. Essa captura de tela é armazenada na nossa base de código, e as próximas execuções de teste vão comparar a captura de tela armazenada com a captura de tela gerada. Se as capturas de tela forem diferentes, o teste vai falhar. Também fornecemos uma flag para executar o teste e forçar uma atualização de captura de tela quando mudamos intencionalmente a renderização e precisamos atualizar o teste.

Os testes de captura de tela não são perfeitos e são um pouco bruscos. Você só pode testar se todo o componente é renderizado como esperado, em vez de afirmações mais específicas. Inicialmente, usamos demais esse recurso para garantir que cada componente (HTML ou tela) fosse renderizado corretamente. Isso diminuiu drasticamente a velocidade do nosso conjunto de testes e causou problemas em que pequenos ajustes de interface quase irrelevantes (como mudanças sutis de cor ou adição de margem entre os itens) causaram falhas em várias capturas de tela e exigiram atualização. Diminuímos o uso de capturas de tela e as usamos exclusivamente para componentes baseados em tela, e isso tem funcionado bem até agora.

Conclusão

A criação do novo painel de insights de desempenho foi uma experiência muito agradável e educativa para a equipe. Aprendemos muito sobre arquivos de rastreamento, trabalho com canvas e muito mais. Esperamos que você goste de usar o novo painel e mal podemos esperar para receber seu feedback.

Para saber mais sobre o painel "Insights de desempenho", consulte Insights de desempenho: receba insights úteis sobre a performance do seu site.

Fazer o download dos canais de visualização

Use o Chrome Canary, Dev ou Beta como navegador de desenvolvimento padrão. Esses canais de pré-lançamento dão acesso aos recursos mais recentes do DevTools, permitem testar APIs modernas da plataforma da Web e ajudam a encontrar problemas no site antes que os usuários o façam!

Entre em contato com a equipe do Chrome DevTools

Use as opções a seguir para discutir novos recursos, atualizações ou qualquer outro item relacionado ao DevTools.