Melhorando o uso de memória do front-end em 30 vezes no carregamento do carrossel

No segundo trimestre de 2020, realizamos uma refatoração técnica na página de experiências, reescrevendo-a em React e utilizando a API REST. Mas, durante a implementação, encontramos um problema interessante.
Existem cerca de 20 carrosséis na página de experiências, cada um com pelo menos 60 blocos. Assim, a página renderizará mais de 1.200 experiências, incluindo informações relevantes como título, avaliação do usuário, usuários simultâneos e miniatura. Em cada carrossel, clicar na barra de rolagem à direita enviará uma solicitação para carregar o próximo lote de experiências, caso haja mais. Em seguida, os novos dados resultantes da solicitação serão anexados ao carrossel, o que significa mais nós adicionados ao DOM e mais miniaturas sendo renderizadas no navegador. O uso de memória dentro do navegador aumentará à medida que os usuários rolarem a tela para solicitar mais dados, e então a experiência do usuário ficará mais lenta e começará a apresentar atrasos.

Perfil de memória
Podemos usar as Ferramentas do desenvolvedor do Chrome para analisar esse problema. O primeiro instantâneo é capturado quando a página é carregada inicialmente, e o segundo é capturado depois que o usuário clica 12 vezes no botão de rolagem seguinte em um carrossel. Observamos um aumento de quase 9,7 MB no uso de memória devido a mais de 100 nós de string e ao aumento na renderização de miniaturas.

Solução
Quando o carrossel rola, os usuários visualizam apenas os itens da janela do carrossel. Por exemplo, em um laptop com resolução de 1920×1080, no modo de tela cheia do navegador, haverá apenas cerca de 9 itens visíveis por vez. Portanto, não é necessário renderizar todos os nós e miniaturas invisíveis juntos nem transportar mais dados retornados da solicitação para o DOM.
Então, eis a ideia: depois de buscarmos os dados brutos da solicitação da API pela primeira vez, construímos uma matriz com o mesmo comprimento, preparando-a para renderização no DOM. Dentro da matriz de renderização, podemos preencher apenas dados suficientes para preencher a tela e avançar para a próxima. O restante da matriz será preenchido apenas quando a janela de exibição for rolada para perto dela. Vamos definir dois índices para registrar o intervalo dos dados de renderização: renderingStartIndex e renderingEndIndex. Configuramos um cursor para indicar qual posição inicial na lista está visível. Ao rolar a tela, precisamos atualizar continuamente o cursor para o índice do primeiro item visível. Antes que a animação de rolagem termine, precisaremos verificar se o próximo cursor está dentro da matriz de dados de renderização. De acordo com o tamanho da janela do carrossel e o tamanho dos cartões, podemos facilmente determinar quantos itens visíveis caberão na janela de exibição. Em seguida, precisamos garantir que os itens da janela atual e da próxima janela estejam dentro do intervalo da matriz de dados de renderização. Caso contrário, pegamos itens dos dados brutos para preencher a lista vazia, ao mesmo tempo em que limpamos os dados anteriores à posição do cursor. Isso se aplica quando os usuários rolam para a direita ou para a esquerda. Quando os usuários rolam para a direita, ao buscar mais dados em segundo plano, preencheremos os dados brutos a cada vez que recebermos novos dados. No entanto, os dados brutos não serão incluídos na renderização da página.


Perfil de memória
Vamos dar uma olhada nas Ferramentas do Desenvolvedor do Chrome novamente. Como antes, o primeiro instantâneo é capturado no momento da inicialização e o segundo é capturado após rolar a tela para a direita 12 vezes. Agora, carregar mais de 100 itens custa apenas 0,3 MB de aumento de memória, em comparação com os 9,7 MB anteriores.

O que vem a seguir?
Além de melhorar o componente carrossel para lidar com janelas de dados grandes, quando refatoramos um recurso/página, precisamos adicionar mais benchmarks para capturar as informações de desempenho.
Embora a pilha de aplicativos web modernos tenha se deslocado para o front-end, capturar e melhorar a telemetria do front-end é tão importante quanto a telemetria tradicional do back-end.
Ying Jiang é engenheira de software front-end sênior na Roblox. Como a primeira engenheira de front-end da Roblox, ela trabalha para introduzir uma pilha de tecnologias front-end modernas na Roblox e melhorar o pipeline de desenvolvimento e implantação. Ela também trabalhou em recursos em tempo real, como chat, fluxo de notificações e voz.
Nem a Roblox Corporation nem este blog endossam ou apoiam qualquer empresa ou serviço. Além disso, não são feitas garantias ou promessas quanto à precisão, confiabilidade ou integridade das informações contidas neste blog.


