Otimizar a interação com a próxima exibição

Saiba como otimizar a interação com a próxima pintura do seu site.

A Interação com a próxima exibição (INP) é uma métrica estável do Core Web Vitals que avalia a capacidade de resposta geral de uma página em relação às interações do usuário observando a latência de todas as interações qualificadas que ocorrem durante a vida útil da visita de um usuário a uma página. O valor final da INP é a interação mais longa observada (às vezes, ignorando valores atípicos).

Para oferecer uma boa experiência ao usuário, os sites devem ter uma interação para a próxima pintura de 200 milissegundos ou menos. Para garantir que essa meta seja atingida para a maioria dos usuários, um bom limite é o 75º percentil de carregamentos de página, segmentado em dispositivos móveis e computadores.

Valores de INP bons são de 200 milissegundos ou menos, valores ruins são maiores que 500 milissegundos, e qualquer valor entre esses dois extremos precisa ser melhorado.

Dependendo do site, pode haver poucas ou nenhuma interação, como páginas com a maioria de texto e imagens com poucos ou nenhum elemento interativo. Ou, no caso de sites como editores de texto ou jogos, pode haver centenas ou até milhares de interações. Em ambos os casos, quando há um INP alto, a experiência do usuário está em risco.

Melhorar o INP exige tempo e esforço, mas a recompensa é uma experiência do usuário melhor. Neste guia, vamos mostrar um caminho para melhorar o INP.

Descubra o que está causando uma INP ruim

Antes de corrigir as interações lentas, você precisa de dados para saber se o INP do seu site é ruim ou precisa de melhorias. Depois de ter essas informações, você pode acessar o laboratório para começar a diagnosticar interações lentas e encontrar uma solução.

Encontrar interações lentas no campo

O ideal é que sua jornada de otimização da INP comece com dados de campo. Na melhor das hipóteses, os dados de campo de um provedor de monitoramento de usuários reais (RUM) vão fornecer não apenas o valor de INP de uma página, mas também dados contextuais que destacam qual interação específica foi responsável pelo valor de INP, se a interação ocorreu durante ou após o carregamento da página, o tipo de interação (clique, tecla ou toque) e outras informações valiosas.

Se você não usa um provedor de RUM para coletar dados de campo, o guia de dados de campo do INP recomenda usar o Chrome User Experience Report (CrUX) pelo PageSpeed Insights para preencher as lacunas. O CrUX é o conjunto de dados oficial do programa Core Web Vitals e fornece um resumo de alto nível das métricas de milhões de sites, incluindo a INP. No entanto, o CrUX geralmente não fornece os dados contextuais que você receberia de um provedor de RUM para ajudar a analisar problemas. Por isso, ainda recomendamos que os sites usem um provedor de RUM sempre que possível ou implementem a própria solução de RUM para complementar o que está disponível no CrUX.

Diagnosticar interações lentas no laboratório

O ideal é começar a testar no laboratório depois de ter dados de campo que sugerem interações lentas. Na ausência de dados de campo, há algumas estratégias para identificar interações lentas no laboratório. Essas estratégias incluem seguir fluxos de usuários comuns e testar interações ao longo do caminho, além de interagir com a página durante o carregamento, quando a linha de execução principal geralmente está mais ocupada, para mostrar interações lentas durante essa parte crucial da experiência do usuário.

Otimizar interações

Depois de identificar uma interação lenta e ser capaz de reproduzi-la manualmente no laboratório, a próxima etapa é otimizar. As interações podem ser divididas em três fases:

  1. O atraso de entrada, que começa quando o usuário inicia uma interação com a página e termina quando os callbacks de eventos para a interação começam a ser executados.
  2. A duração do processamento, que consiste no tempo que os callbacks de eventos levam para serem executados até a conclusão.
  3. O atraso de apresentação, que é o tempo que o navegador leva para apresentar o próximo frame que contém o resultado visual da interação.

A soma dessas três fases é a latência total de interação. Cada fase de uma interação contribui com um tempo para a latência total da interação. Por isso, é importante saber como otimizar cada parte da interação para que ela seja executada pelo menor tempo possível.

Identificar e reduzir a latência de entrada

Quando um usuário interage com uma página, a primeira parte dessa interação é o input delay. Dependendo de outras atividades na página, os atrasos de entrada podem ser consideráveis. Isso pode ser devido à atividade que ocorre na linha de execução principal (talvez devido ao carregamento, análise e compilação de scripts), ao processamento de busca, às funções do timer ou até mesmo a outras interações que ocorrem em sucessão rápida e se sobrepõem.

Seja qual for a origem do delay de entrada de uma interação, é recomendável reduzir o delay de entrada ao mínimo para que as interações possam começar a executar callbacks de evento o mais rápido possível.

A relação entre a avaliação do script e as tarefas demoradas durante a inicialização

Um aspecto essencial da interatividade no ciclo de vida da página é durante a inicialização. À medida que uma página é carregada, ela é renderizada inicialmente, mas é importante lembrar que o fato de uma página ter sido renderizada não significa que o carregamento dela foi concluído. Dependendo de quantos recursos uma página precisa para ficar totalmente funcional, é possível que os usuários tentem interagir com a página enquanto ela ainda está carregando.

Uma coisa que pode estender o atraso de entrada de uma interação enquanto uma página é carregada é a avaliação do script. Depois que um arquivo JavaScript é buscado da rede, o navegador ainda precisa fazer algo antes que o JavaScript possa ser executado. Esse trabalho inclui analisar um script para garantir que a sintaxe seja válida, compilar em bytecode e, por fim, executar.

Dependendo do tamanho de um script, esse trabalho pode introduzir tarefas longas na linha de execução principal, o que atrasará a resposta do navegador a outras interações do usuário. Para manter a página responsiva à entrada do usuário durante o carregamento, é importante entender o que você pode fazer para reduzir a probabilidade de tarefas longas durante o carregamento, para que a página continue rápida.

Otimizar callbacks de eventos

O atraso de entrada é apenas a primeira parte do que o INP mede. Também é necessário garantir que os callbacks de evento executados em resposta a uma interação do usuário sejam concluídos o mais rápido possível.

Ceda à linha de execução principal com frequência

O melhor conselho geral para otimizar callbacks de eventos é fazer o mínimo possível de trabalho neles. No entanto, sua lógica de interação pode ser complexa, e você só pode reduzir marginalmente o trabalho que ela faz.

Se esse for o caso do seu site, tente dividir o trabalho em callbacks de eventos em tarefas separadas. Isso evita que o trabalho coletivo se torne uma tarefa longa que bloqueia a linha de execução principal, o que permite que outras interações que estariam aguardando a linha de execução principal sejam executadas mais cedo.

setTimeout é uma maneira de dividir tarefas, porque o callback transmitido é executado em uma nova tarefa. Você pode usar setTimeout sozinho ou abstrair o uso em uma função separada para um rendimento mais ergonômico.

O rendimento indiscriminado é melhor do que não renderizar nada. No entanto, há uma maneira mais sutil de renderizar a linha de execução principal, que envolve renderizar apenas após um callback de evento que atualiza a interface do usuário para que a lógica de renderização seja executada mais cedo.

Rendimento para permitir que o trabalho de renderização ocorra mais cedo

Uma técnica de rendimento mais avançada envolve estruturar o código nos callbacks de eventos para limitar o que é executado apenas à lógica necessária para aplicar atualizações visuais para o próximo frame. O restante pode ser adiado para uma tarefa posterior. Isso não apenas mantém os callbacks leves e ágeis, mas também melhora o tempo de renderização das interações, não permitindo que as atualizações visuais bloqueiem o código de callback do evento.

Por exemplo, imagine um editor de texto avançado que formata o texto conforme você digita, mas também atualiza outros aspectos da interface em resposta ao que você escreveu, como contagem de palavras, destaque de erros de ortografia e outros feedbacks visuais importantes. Além disso, o aplicativo também pode salvar o que você escreveu para que, se você sair e voltar, não perca o trabalho.

Neste exemplo, as quatro coisas a seguir precisam acontecer em resposta aos caracteres digitados pelo usuário. No entanto, apenas o primeiro item precisa ser feito antes que o próximo frame seja apresentado.

  1. Atualize a caixa de texto com o que o usuário digitou e aplique a formatação necessária.
  2. Atualize a parte da interface que mostra a contagem de palavras atual.
  3. Execute a lógica para verificar erros de ortografia.
  4. Salve as alterações mais recentes (localmente ou em um banco de dados remoto).

O código para fazer isso pode ficar mais ou menos assim:

textBox.addEventListener('input', (inputEvent) => {
 
// Update the UI immediately, so the changes the user made
 
// are visible as soon as the next frame is presented.
  updateTextBox
(inputEvent);

 
// Use `setTimeout` to defer all other work until at least the next
 
// frame by queuing a task in a `requestAnimationFrame()` callback.
  requestAnimationFrame
(() => {
    setTimeout
(() => {
     
const text = textBox.textContent;
      updateWordCount
(text);
      checkSpelling
(text);
      saveChanges
(text);
   
}, 0);
 
});
});

A visualização a seguir mostra como adiar as atualizações não críticas até depois do próximo frame pode reduzir a duração do processamento e, portanto, a latência geral da interação.

Uma representação de uma interação do teclado e tarefas subsequentes em dois cenários. Na figura de cima, a tarefa crítica de renderização e todas as tarefas em segundo plano subsequentes são executadas de forma síncrona até que a oportunidade de apresentar um frame chegue. Na figura de baixo, o trabalho crítico de renderização é executado primeiro e, em seguida, é enviado para a linha de execução principal para apresentar um novo frame mais cedo. As tarefas em segundo plano são executadas depois disso.
Clique na figura acima para conferir uma versão em alta resolução.

O uso de setTimeout() dentro de uma chamada requestAnimationFrame() no exemplo de código anterior é um pouco esotérico, mas é um método eficaz que funciona em todos os navegadores para garantir que o código não crítico não bloqueie o próximo frame.

Evite a sobrecarga de layout

A troca frequente de layout, às vezes chamada de layout síncrono forçado, é um problema de performance de renderização em que o layout ocorre de forma síncrona. Ele ocorre quando você atualiza estilos em JavaScript e os lê na mesma tarefa. Há muitas propriedades no JavaScript que podem causar o thrashing do layout.

Uma visualização do thrashing de layout, mostrada no painel de performance do Chrome DevTools.
Exemplo de thrashing de layout, conforme mostrado no painel de desempenho do Chrome DevTools. As tarefas de renderização que envolvem o conflito de layout serão notadas com um triângulo vermelho no canto superior direito da parte da pilha de chamadas, geralmente rotulado como Recalculate Style ou Layout.

A troca frequente de layout é um gargalo de performance porque, ao atualizar estilos e solicitar imediatamente os valores desses estilos em JavaScript, o navegador é forçado a fazer um trabalho de layout síncrono que poderia ter esperado para executar de forma assíncrona depois que os callbacks de eventos tivessem terminado de ser executados.

Minimizar o atraso na apresentação

O delay de apresentação de uma interação marca o período entre o término da execução dos callbacks de evento de uma interação e o momento em que o navegador consegue pintar o próximo frame que mostra as mudanças visuais resultantes.

Minimizar o tamanho do DOM

Quando o DOM de uma página é pequeno, o trabalho de renderização geralmente é concluído rapidamente. No entanto, quando os DOMs ficam muito grandes, o trabalho de renderização tende a ser dimensionado com o aumento do tamanho do DOM. A relação entre o trabalho de renderização e o tamanho do DOM não é linear, mas DOMs grandes exigem mais trabalho para renderização do que DOMs pequenos. Um DOM grande é problemático em dois casos:

  1. Durante a renderização inicial da página, em que um DOM grande exige muito trabalho para renderizar o estado inicial da página.
  2. Em resposta a uma interação do usuário, em que um DOM grande pode fazer com que as atualizações de renderização sejam muito caras e, portanto, aumentem o tempo que o navegador leva para apresentar o próximo frame.

Lembre-se de que há casos em que DOMs grandes não podem ser reduzidos significativamente. Embora existam abordagens que você pode usar para reduzir o tamanho do DOM, como achatar o DOM ou adicionar ao DOM durante as interações do usuário para manter o tamanho inicial do DOM pequeno, essas técnicas podem ser limitadas.

Use content-visibility para renderizar de modo preguiçoso elementos fora da tela

Uma maneira de limitar a quantidade de trabalho de renderização durante o carregamento da página e em resposta às interações do usuário é usar a propriedade CSS content-visibility, que equivale a renderizar elementos de maneira lenta à medida que eles se aproximam da viewport. Embora o uso eficaz de content-visibility exija alguma prática, vale a pena investigar se o resultado é um tempo de renderização menor que pode melhorar a INP da sua página.

Conheça os custos de desempenho ao renderizar HTML usando JavaScript

Onde há HTML, há análise de HTML. Depois que o navegador termina de analisar o HTML em um DOM, ele precisa aplicar estilos, realizar cálculos de layout e renderizar esse layout. Esse é um custo inevitável, mas a maneira como você renderiza HTML é importante.

Quando o servidor envia HTML, ele chega ao navegador como um fluxo. O streaming significa que a resposta HTML do servidor está chegando em partes. O navegador otimiza a forma como processa um stream analisando incrementos de pedaços desse stream à medida que eles chegam e renderizando-os bit a bit. Essa é uma otimização de desempenho em que o navegador é renderizado implicitamente de forma periódica e automática durante o carregamento da página, e você recebe isso sem custo financeiro.

Embora a primeira visita a qualquer site sempre envolva alguns elementos HTML, uma abordagem comum começa com um mínimo inicial de HTML e, em seguida, o JavaScript é usado para preencher a área de conteúdo. As atualizações subsequentes nessa área de conteúdo também ocorrem como resultado das interações do usuário. Isso geralmente é chamado de modelo de aplicativo de página única (SPA). Uma desvantagem desse padrão é que, ao renderizar HTML com JavaScript no cliente, você não tem apenas o custo do processamento do JavaScript para criar esse HTML, mas também o navegador não renderiza até que ele tenha terminado de analisar esse HTML e renderizá-lo.

No entanto, é importante lembrar que mesmo sites que não são SPAs provavelmente envolvem alguma renderização de HTML usando JavaScript como resultado das interações. Isso geralmente é aceitável, desde que você não renderize grandes quantidades de HTML no cliente, o que pode atrasar a apresentação do próximo frame. No entanto, é importante entender as implicações de performance dessa abordagem para renderizar HTML no navegador e como isso pode afetar a capacidade de resposta do site à entrada do usuário se você estiver renderizando muito HTML usando JavaScript.

Conclusão

Melhorar o INP do seu site é um processo iterativo. Quando você corrige uma interação lenta no campo, é provável que encontre outras interações lentas, especialmente se o site tiver muita interatividade. Você vai precisar otimizar essas interações também.

A chave para melhorar a INP é a persistência. Com o tempo, você pode deixar a página responsiva para que os usuários fiquem satisfeitos com a experiência que você está oferecendo. Também é provável que, ao desenvolver novos recursos para os usuários, você precise passar pelo mesmo processo de otimização de interações específicas para eles. Isso vai levar tempo e esforço, mas é um tempo e esforço bem gastos.

Imagem principal do Unsplash, por David Pisnoy e modificada de acordo com a licença do Unsplash.