Métodos para melhorar e otimizar o desempenho em aplicativos React
Publicados: 2022-03-10O React permite que aplicativos da Web atualizem suas interfaces de usuário (UIs) rapidamente, mas isso não significa que seu aplicativo React de médio ou grande porte terá um desempenho eficiente. Seu desempenho dependerá de como você usa o React ao construí-lo e do seu entendimento de como o React opera e do processo pelo qual os componentes passam pelas várias fases de seu ciclo de vida. O React oferece muitas melhorias de desempenho para um aplicativo da Web, e você pode obter essas melhorias por meio de várias técnicas, recursos e ferramentas.
Neste tutorial, discutiremos vários métodos de otimização de desempenho em aplicativos React e também os recursos do React que podemos usar para melhorar o desempenho.
Por onde começar a otimizar o desempenho em um aplicativo React?
Não podemos começar a otimizar um aplicativo sem saber exatamente quando e onde otimizar. Você pode estar se perguntando: “Por onde começamos?”
Durante o processo de renderização inicial, o React constrói uma árvore de componentes DOM. Então, quando os dados mudam na árvore DOM, queremos que o React re-renderize apenas os componentes que foram afetados pela mudança, pulando os outros componentes da árvore que não foram afetados.
No entanto, o React pode acabar renderizando novamente todos os componentes na árvore DOM, mesmo que nem todos sejam afetados. Isso resultará em tempo de carregamento mais longo, tempo desperdiçado e até mesmo desperdício de recursos de CPU. Precisamos evitar que isso aconteça. Então, é aqui que vamos focar nosso esforço de otimização.
Nesta situação, poderíamos configurar cada componente para apenas renderizar ou diferenciar quando necessário, para evitar desperdício de recursos e tempo.
Medindo o Desempenho
Nunca inicie o processo de otimização do seu aplicativo React baseado no que você sente. Em vez disso, use as ferramentas de medição disponíveis para analisar o desempenho do seu aplicativo React e obter um relatório detalhado do que pode estar deixando-o lento.
Analisando componentes do React com a guia Desempenho do Chrome
De acordo com a documentação do React, enquanto você ainda está no modo de desenvolvimento, você pode usar a guia “Performance” no navegador Chrome para visualizar como os componentes do React são montados, atualizados e desmontados. Por exemplo, a imagem abaixo mostra o perfil da guia “Desempenho” do Chrome e analisando meu blog no modo de desenvolvimento.
Para fazer isso, siga estas etapas:
- Desabilite todas as extensões temporariamente, principalmente as React Developer Tools, pois elas podem atrapalhar o resultado da análise. Você pode facilmente desabilitar extensões executando seu navegador no modo de navegação anônima.
- Verifique se o aplicativo está sendo executado no modo de desenvolvimento. Ou seja, o aplicativo deve estar sendo executado em seu localhost.
- Abra as Ferramentas do desenvolvedor do Chrome, clique na guia "Desempenho" e clique no botão "Gravar".
- Execute as ações que você deseja criar o perfil. Não grave mais de 20 segundos, senão o Chrome pode travar.
- Pare a gravação.
- Os eventos do React serão agrupados sob o rótulo "Tempo do usuário".
Os números do criador de perfil são relativos. A maioria das vezes e os componentes serão renderizados mais rapidamente na produção. No entanto, isso deve ajudá-lo a descobrir quando a interface do usuário é atualizada por engano, bem como a profundidade e a frequência com que as atualizações da interface do usuário ocorrem.
React Developer Tools Profiler
De acordo com a documentação do React, no react react-dom
16.5+ e react react-native
0.57+, recursos aprimorados de criação de perfil estão disponíveis no modo de desenvolvedor usando o React Developer Tools Profiler. O criador de perfil usa a API do Profiler experimental do React para reunir informações de tempo sobre cada componente renderizado, a fim de identificar gargalos de desempenho em um aplicativo React.
Basta baixar o React Developer Tools para o seu navegador e, em seguida, você pode usar a ferramenta de criação de perfil que acompanha ele. O criador de perfil só pode ser usado no modo de desenvolvimento ou na criação de perfil de produção do React v16.5+. A imagem abaixo é o resumo do profiler do meu blog em modo de desenvolvimento usando o React Developer Tools Profiler:
Para conseguir isso, siga estas etapas:
- Baixe as Ferramentas do Desenvolvedor React.
- Certifique-se de que seu aplicativo React esteja no modo de desenvolvimento ou na criação de perfil de produção do React v16.5+.
- Abra a guia "Ferramentas do desenvolvedor" do Chrome. Uma nova aba chamada “Profiler” estará disponível, fornecida pelo React Developer Tools.
- Clique no botão “Gravar” e execute as ações que deseja criar o perfil. Idealmente, pare de gravar depois de executar as ações que deseja criar o perfil.
- Um gráfico (conhecido como flamegraph) aparecerá com todos os manipuladores de eventos e componentes do seu aplicativo React.
Nota : Consulte a documentação para obter mais informações.
Memorização com React.memo()
O React v16 foi lançado com uma API adicional, um componente de ordem superior chamado React.memo()
. De acordo com a documentação, isso existe apenas como otimização de desempenho .
Seu nome, “ memo ” vem de memoization, que é basicamente uma forma de otimização usada principalmente para acelerar o código armazenando os resultados de chamadas de funções caras e retornando o resultado armazenado sempre que a mesma função cara for chamada novamente.
A memorização é uma técnica para executar uma função uma vez, geralmente uma função pura, e depois salvar o resultado na memória. Se tentarmos executar essa função novamente, com os mesmos argumentos de antes , ela apenas retornará o resultado salvo anteriormente da execução da primeira função, sem executar a função novamente.
Mapeando a descrição acima para o ecossistema React, as funções mencionadas são componentes React e os argumentos são props.
O comportamento padrão de um componente declarado usando React.memo()
é que ele renderiza somente se as props no componente forem alteradas. Ele faz uma comparação superficial dos adereços para verificar isso, mas uma opção está disponível para substituir isso.
React.memo()
aumenta o desempenho de um aplicativo React evitando a rerenderização de componentes cujas props não foram alteradas ou quando a rerenderização não é necessária.
O código abaixo é a sintaxe básica de React.memo()
:
const MemoizedComponent = React.memo((props) => { // Component code goes in here })
Quando usar React.memo()
- Componente funcional puro
Você pode usarReact.memo()
se seu componente for funcional, receber as mesmas props e sempre renderizar a mesma saída. Você também pode usarReact.memo()
em componentes funcionais não puros com ganchos React. - O componente renderiza frequentemente
Você pode usarReact.memo()
para encapsular um componente que é renderizado com frequência. - O componente renderiza novamente com os mesmos adereços
UseReact.memo()
para encapsular um componente que geralmente é fornecido com as mesmas props durante a re-renderização. - Elementos médios a altos
Use-o para um componente que contém um número médio a alto de elementos de interface do usuário para verificar a igualdade das props.
Nota : Tenha cuidado ao memorizar componentes que usam props como callbacks. Certifique-se de usar a mesma instância de função de retorno de chamada entre as renderizações. Isso ocorre porque o componente pai pode fornecer diferentes instâncias da função de retorno de chamada em cada renderização, o que fará com que o processo de memorização seja interrompido. Para corrigir isso, certifique-se de que o componente memoizado sempre receba a mesma instância de retorno de chamada.
Vamos ver como podemos usar a memoização em uma situação do mundo real. O componente funcional abaixo, chamado “Photo”, usa React.memo()
para evitar re-renderização.
export function Photo({ title, views }) { return ( <div> <div>Photo title: {title}</div> <div>Location: {location}</div> </div> ); } // memoize the component export const MemoizedPhoto = React.memo(Photo);
O código acima consiste em um componente funcional que exibe uma div contendo o título da foto e a localização do assunto na foto. Também estamos memorizando o componente criando uma nova função e chamando-a de MemoizedPhoto
. Memorizar o componente de foto impedirá que o componente seja renderizado novamente, desde que os adereços, o title
e o location
sejam os mesmos nas renderizações subsequentes.
// On first render, React calls MemoizedPhoto function. <MemoizedPhoto title="Effiel Tower" location="Paris" /> // On next render, React does not call MemoizedPhoto function, // preventing rendering <MemoizedPhoto title="Effiel Tower" location="Paris" />
Aqui, o React chama a função memorizada apenas uma vez. Ele não renderizará o componente na próxima chamada, desde que os adereços permaneçam os mesmos.
Agrupamento e minificação
Em aplicativos de página única React, podemos agrupar e reduzir todo o nosso código JavaScript em um único arquivo. Tudo bem, desde que nosso aplicativo seja relativamente pequeno.
À medida que nosso aplicativo React cresce, agrupar e reduzir todo o nosso código JavaScript em um único arquivo se torna problemático, difícil de entender e tedioso. Isso também afetará o desempenho e o tempo de carregamento do nosso aplicativo React porque estamos enviando um grande arquivo JavaScript para o navegador. Portanto, precisamos de algum processo para nos ajudar a dividir a base de código em vários arquivos e entregá-los ao navegador em intervalos conforme necessário.
Em uma situação como essa, podemos usar alguma forma de empacotador de ativos como o Webpack e, em seguida, aproveitar sua funcionalidade de divisão de código para dividir nosso aplicativo em vários arquivos.
A divisão de código é sugerida na documentação do Webpack como um meio de melhorar o tempo de carregamento de um aplicativo. Também é sugerido na documentação do React para carregamento lento (servindo apenas as coisas atualmente necessárias ao usuário), o que pode melhorar drasticamente o desempenho.
O Webpack sugere três abordagens gerais para divisão de código:
- Pontos de entrada
Divida o código manualmente usando a configuração de entrada. - Prevenção de duplicação
UseSplitChunksPlugin
para desduplicar e dividir pedaços. - Importações dinâmicas
Código dividido por meio de chamadas de função inline dentro dos módulos.
Benefícios da divisão de código
- A divisão do código auxilia nos recursos de cache do navegador e no código que não muda com frequência.
- Também ajuda o navegador a baixar recursos em paralelo, o que reduz o tempo geral de carregamento do aplicativo.
- Ele nos permite dividir o código em pedaços que serão carregados sob demanda ou conforme a necessidade do aplicativo.
- Ele mantém o download inicial de recursos na primeira renderização relativamente pequeno, reduzindo assim o tempo de carregamento do aplicativo.
Estruturas de dados imutáveis
A documentação do React fala do poder de não alterar dados. Todos os dados que não podem ser alterados são imutáveis. Imutabilidade é um conceito que os programadores React devem entender.
Um valor ou objeto imutável não pode ser alterado. Assim, quando há uma atualização, um novo valor é criado na memória, deixando o antigo intocado.
Podemos usar estruturas de dados imutáveis e React.PureComponent
para verificar automaticamente uma mudança de estado complexa. Por exemplo, se o estado em seu aplicativo for imutável, você poderá salvar todos os objetos de estado em um único armazenamento com uma biblioteca de gerenciamento de estado como o Redux, permitindo que você implemente facilmente a funcionalidade de desfazer e refazer.
Não se esqueça de que não podemos alterar dados imutáveis depois de criados.
Benefícios das estruturas de dados imutáveis
- Eles não têm efeitos colaterais.
- Objetos de dados imutáveis são fáceis de criar, testar e usar.
- Eles nos ajudam a escrever a lógica que pode ser usada para verificar rapidamente as atualizações no estado, sem ter que verificar os dados repetidamente.
- Eles ajudam a evitar o acoplamento temporal (um tipo de acoplamento no qual o código depende da ordem de execução).
As seguintes bibliotecas ajudam a fornecer um conjunto de estruturas de dados imutáveis:
- ajudante de imutabilidade
Mude uma cópia de dados sem alterar a fonte. - Imutável.js
Coleções de dados persistentes imutáveis para JavaScript aumentam a eficiência e a simplicidade. - imutável sem emenda
Estruturas de dados imutáveis para JavaScript tornam-se compatíveis com matrizes e objetos JavaScript normais. - Reagir-copiar-escrever
Isso fornece um estado imutável com uma API mutável.
Outros métodos para melhorar o desempenho
Use uma compilação de produção antes da implantação
A documentação do React sugere o uso da compilação de produção minificada ao implantar seu aplicativo.
Evite funções anônimas
Como funções anônimas não recebem um identificador (via const/let/var
), elas não são persistentes sempre que um componente inevitavelmente é renderizado novamente. Isso faz com que o JavaScript aloque nova memória cada vez que esse componente é renderizado novamente, em vez de alocar um único pedaço de memória apenas uma vez, como quando funções nomeadas estão sendo usadas.
import React from 'react'; // Don't do this. class Dont extends Component { render() { return ( <button onClick={() => console.log('Do not do this')}> Don't </button> ); } } // The better way class Do extends Component { handleClick = () => { console.log('This is OK'); } render() { return ( <button onClick={this.handleClick}> Do </button> ); } }
O código acima mostra duas maneiras diferentes de fazer um botão executar uma ação ao clicar. O primeiro bloco de código usa uma função anônima na prop onClick()
, e isso afetaria o desempenho. O segundo bloco de código usa uma função nomeada na função onClick()
, que é a maneira correta neste cenário.
Montar e desmontar componentes muitas vezes é caro
Usar condicionais ou tenários para fazer um componente desaparecer (ou seja, desmontá-lo) não é aconselhável, porque o componente feito para desaparecer fará com que o navegador redesenhe e reflua. Este é um processo caro porque as posições e geometrias dos elementos HTML no documento terão que ser recalculadas. Em vez disso, podemos usar as propriedades de opacity
e visibility
do CSS para ocultar o componente. Desta forma, o componente ainda estará no DOM mas invisível, sem nenhum custo de performance.
Virtualizar listas longas
A documentação sugere que, se você estiver renderizando uma lista com uma grande quantidade de dados, deverá renderizar uma pequena parte dos dados na lista de cada vez na janela de visualização visível. Então, você pode renderizar mais dados conforme a lista está sendo rolada; portanto, os dados são exibidos somente quando estão na viewport. Esse processo é chamado de “janela”. Na janela, um pequeno subconjunto de linhas é renderizado a qualquer momento. Existem bibliotecas populares para fazer isso, duas das quais são mantidas por Brian Vaughn:
- janela de reação
- reagir-virtualizado
Conclusão
Existem vários outros métodos para melhorar o desempenho do seu aplicativo React. Este artigo discutiu os métodos mais importantes e eficazes de otimização de desempenho.
Espero que você tenha gostado de ler este tutorial. Você pode aprender mais através dos recursos listados abaixo. Se você tiver alguma dúvida, deixe-a na seção de comentários abaixo. Terei todo o gosto em responder a cada uma delas.
Referências e recursos relacionados
- “Otimizando o desempenho”, React Docs
- “Use React.memo com sabedoria”, Dmitri Pavlutin
- “Técnicas de otimização de desempenho em React”, Niteesh Yadav
- “Imutabilidade no React: Não há nada de errado com objetos mutantes”, Esteban Herrera
- “10 maneiras de otimizar o desempenho do seu aplicativo React”, Chidume Nnamdi
- “5 dicas para melhorar o desempenho de seus aplicativos React”, William Le