A entrada está chegando ao compositor
Esta é a última da série de blogs em quatro partes sobre o Chrome. Vamos investigar como ela lida com o código para exibir um site. Na postagem anterior, analisamos o processo de renderização e aprendemos sobre o compositor. Nesta postagem, vamos analisar como o compositor permite uma interação suave quando a entrada do usuário é recebida.
Eventos de entrada do ponto de vista do navegador
Quando você ouve "eventos de entrada", pode pensar apenas em digitar em uma caixa de texto ou clicar com o mouse, mas, do ponto de vista do navegador, entrada significa qualquer gesto do usuário. A rolagem da roda do mouse é um evento de entrada, e o toque ou o passar do mouse também é um evento de entrada.
Quando um gesto do usuário, como tocar na tela, ocorre, o processo do navegador é o que recebe o
gesto primeiro. No entanto, o processo do navegador só sabe onde esse gesto ocorreu, já que
o conteúdo dentro de uma guia é processado pelo processo do renderizador. Assim, o processo do navegador envia o tipo
de evento (como touchstart
) e as coordenadas para o processo do renderizador. O processo do renderizador processa o evento adequadamente, encontrando o destino dele e executando os listeners do evento anexados.
O criador recebe eventos de entrada
Na postagem anterior, mostramos como o compositor poderia processar a rolagem de forma suave com a composição de camadas rasterizadas. Se nenhum listener de evento de entrada estiver anexado à página, a linha de execução do compositor poderá criar um novo frame composto completamente independente da linha de execução principal. Mas e se alguns listeners de eventos fossem anexados à página? Como a linha de execução do compositor descobriria se o evento precisa ser processado?
Noções básicas sobre a região de rolagem não rápida
Como a execução do JavaScript é o trabalho da linha de execução principal, quando uma página é composta, a linha de execução do compositor marca uma região da página que tem manipuladores de eventos anexados como "Região não rolável rápida". Com essas informações, a linha de execução do compositor pode enviar o evento de entrada para a linha de execução principal se o evento ocorrer nessa região. Se o evento de entrada vier de fora dessa região, a linha de execução do compositor vai continuar compondo um novo frame sem esperar pela linha de execução principal.
Tenha cuidado ao escrever manipuladores de eventos
Um padrão de manipulação de eventos comum no desenvolvimento da Web é a delegação de eventos. Como os eventos são formados no balão, você pode anexar um manipulador de eventos ao elemento de nível superior e delegar tarefas com base no destino do evento. Você pode ter visto ou escrito um código como o abaixo.
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault();
}
});
Como você só precisa escrever um gerenciador de eventos para todos os elementos, a ergonomia desse padrão de delegação de eventos é atraente. No entanto, se você analisar esse código do ponto de vista do navegador, a página inteira será marcada como uma região de rolagem não rápida. Isso significa que, mesmo que o aplicativo não se importe com a entrada de determinadas partes da página, a linha de execução do compositor precisa se comunicar com a linha de execução principal e esperar por ela sempre que um evento de entrada chegar. Assim, a capacidade de rolagem suave do compositor é anulada.
Para evitar que isso aconteça, transmita as opções passive: true
no listener
de eventos. Isso indica ao navegador que você ainda quer ouvir o evento na linha de execução principal,
mas o compositor pode continuar e compor um novo frame também.
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault()
}
}, {passive: true});
Verificar se o evento pode ser cancelado
Imagine que você tem uma caixa em uma página que quer limitar a direção de rolagem apenas para rolagem horizontal.
Usar a opção passive: true
no evento do ponteiro significa que a rolagem da página pode ser suave, mas
a rolagem vertical pode ter começado no momento em que você quer preventDefault
para limitar
a direção da rolagem. Para conferir isso, use o método event.cancelable
.
document.body.addEventListener('pointermove', event => {
if (event.cancelable) {
event.preventDefault(); // block the native scroll
/*
* do what you want the application to do here
*/
}
}, {passive: true});
Como alternativa, use uma regra CSS como touch-action
para eliminar completamente o manipulador de eventos.
#area {
touch-action: pan-x;
}
Como encontrar o destino do evento
Quando a linha de execução do compositor envia um evento de entrada para a linha de execução principal, a primeira coisa a ser executada é um teste de hit para encontrar o destino do evento. O teste de hit usa dados de registros de pintura gerados no processo de renderização para descobrir o que está abaixo das coordenadas do ponto em que o evento ocorreu.
Como minimizar os envios de eventos para a linha de execução principal
Na postagem anterior, discutimos como nossa tela de exibição típica atualiza a tela 60 vezes por segundo e como precisamos acompanhar a cadência para uma animação suave. Para entrada, um dispositivo touchscreen típico gera eventos de toque de 60 a 120 vezes por segundo, e um mouse típico gera eventos 100 vezes por segundo. O evento de entrada tem uma fidelidade maior do que a tela pode atualizar.
Se um evento contínuo, como touchmove
, for enviado para a linha de execução principal 120 vezes por segundo, ele
poderá acionar uma quantidade excessiva de testes de hit e execução do JavaScript em comparação com a velocidade de
atualização da tela.
Para minimizar chamadas excessivas na linha de execução principal, o Chrome combina eventos contínuos (como
wheel
, mousewheel
, mousemove
, pointermove
e touchmove
) e atrasa o envio até
pouco antes do próximo requestAnimationFrame
.
Qualquer evento discreto, como keydown
, keyup
, mouseup
, mousedown
, touchstart
e touchend
,
é enviado imediatamente.
Use getCoalescedEvents
para receber eventos intraframe
Para a maioria dos aplicativos da Web, os eventos agrupados são suficientes para proporcionar uma boa experiência do usuário.
No entanto, se você estiver criando itens como um aplicativo de desenho e colocando um caminho com base em
coordenadas touchmove
, talvez perca as coordenadas entre elas para desenhar uma linha suave. Nesse caso,
é possível usar o método getCoalescedEvents
no evento do ponteiro para receber informações sobre esses
eventos agrupados.
window.addEventListener('pointermove', event => {
const events = event.getCoalescedEvents();
for (let event of events) {
const x = event.pageX;
const y = event.pageY;
// draw a line using x and y coordinates.
}
});
Próximas etapas
Nesta série, abordamos o funcionamento interno de um navegador da Web. Se você nunca pensou em por que
o DevTools recomenda adicionar {passive: true}
ao seu manipulador de eventos ou por que você pode escrever o atributo async
na tag de script, esperamos que esta série esclareça por que um navegador precisa dessas
informações para oferecer uma experiência na Web mais rápida e suave.
Usar o Lighthouse
Se você quer que seu código seja bom para o navegador, mas não sabe por onde começar, o Lighthouse é uma ferramenta que realiza auditorias em qualquer site e gera um relatório sobre o que está sendo feito corretamente e o que precisa de melhorias. Ler a lista de auditorias também dá uma ideia do que é importante para um navegador.
Saiba como medir a performance
Os ajustes de performance podem variar para sites diferentes. Por isso, é fundamental medir a performance do seu site e decidir o que é melhor para ele. A equipe do Chrome DevTools tem alguns tutoriais sobre como medir o desempenho do seu site.
Adicionar uma política de recursos ao seu site
Se quiser ir além, a Política de recursos é um novo recurso da plataforma da Web que pode ser uma proteção ao criar seu projeto. Ativar
a política de recursos garante o comportamento específico do app e evita erros.
Por exemplo, se você quiser garantir que o app nunca bloqueie a análise, execute-o na
política de scripts síncronos. Quando sync-script: 'none'
está ativado, o JavaScript que bloqueia o analisador
não pode ser executado. Isso impede que o código bloqueie o analisador, e o
navegador não precisa se preocupar em pausar o analisador.
Conclusão
Quando comecei a criar sites, eu só me preocupava em como escrever o código e o que me ajudaria a ser mais produtivo. Essas coisas são importantes, mas também precisamos pensar em como o navegador recebe o código que escrevemos. Os navegadores modernos têm investido e continuam investindo em maneiras de proporcionar uma experiência da Web melhor para os usuários. Ser simpático com o navegador organizando nosso código melhora a experiência do usuário. Espero que você se junte a mim na missão de ser gentil com os navegadores.
Agradecemos muito a todos que revisaram os primeiros rascunhos desta série, incluindo, mas não se limitando a: Alex Russell, Paul Ireland, Meggin Kearney, Eric Bidelman, Mathias Bynens, Addy Osmani e Kinuko Yaskoisuda.
Você gostou da série? Se você tiver dúvidas ou sugestões para a próxima postagem, entre em contato comigo na seção de comentários abaixo ou pelo @kosamari no Twitter.