Análise e otimização de aplicativos React

Pessoas como eu, que lutam por sites de alto desempenho, costumam gastar muito tempo nisso. Agora, vou resolver de uma vez por todas o problema dos recursos da web de baixa velocidade cuja interface está escrita em React. Ou seja, sugiro que todos que leem isso parem de usar o React hoje.



O autor do material, cuja tradução publicamos hoje, é claro, brinca. Aqui, falaremos sobre como otimizar o desempenho dos aplicativos React. A propósito, antes de começar, vamos pensar por que a otimização de sites geralmente é necessária. Talvez possamos dizer que é necessário para que o site seja usado por mais pessoas do que antes da otimização.

1. Introdução


Como otimizar o site? Como você pode avaliar os benefícios da otimização para os usuários do site? E por que você precisa pensar em tais indicadores?

Vamos tentar responder a essas perguntas, procurando a maneira mais fácil de criar aplicativos React - usando a ferramenta create-react-app (CRA). Um novo projeto criado usando esta ferramenta possui as seguintes dependências:

  • A principal biblioteca de react , que permite trabalhar com os componentes do React: 2,5 Kb.
  • A biblioteca react-dom , que permite que os componentes sejam exibidos na página, transformando-os em estruturas adequadas para inserção na árvore DOM: 30,7 Kb.
  • Uma pequena quantidade de código, que inclui o modelo do primeiro componente: cerca de 3 Kb.

Estes dados são obtidos para o React 16.6.3.

Para descobrir quanto tempo levará para baixar um novo aplicativo CRA no seu telefone Moto G4, você pode usar o serviço WebPageTest .


Tempo de carregamento do site no Moto G4 usando redes diferentes

Este aplicativo, uma variação de "Hello, World", está hospedado no Firebase, está sendo investigado o carregamento no navegador Chrome usando três tipos de conexão:

  • 4G (9 Mbps)
  • 3G (1,6 Mbps)
  • Conexão 3G lenta (400 Kbps)

Aqui você precisa considerar os atrasos na rede.

Por que usei o Moto G4 para o experimento? Este é um telefone simples e barato, semelhante aos telefones que, na forma de dispositivos básicos, são usados ​​por muitas pessoas nos países em desenvolvimento. Em uma rede 4G, o aplicativo é carregado em 2 segundos. Em uma rede 3G lenta, foram necessários mais de 4 segundos para uma página ficar online.

Embora essas métricas sejam bastante interessantes, elas não serão particularmente úteis se você não souber quem são seus usuários. O que você define como "lento" pode ser completamente diferente do que eu ou outra pessoa consideramos "lento". E sua percepção do carregamento "rápido" do site pode ficar distorcida pelo dispositivo e pela conexão de rede que você usa. Se você incluir um computador desktop conectado à Internet por meio de uma conexão com fio neste experimento, poderá ver a diferença entre o carregamento "rápido" e o "lento" de um site.


Tempo de carregamento do site no computador desktop e no Moto G4

Para melhorar o desempenho dos aplicativos React, cuja construção utiliza a biblioteca React, como eles dizem, “fora da caixa”, são descritas as melhorias do React DOM, que visam simplificar algumas coisas. Portanto, o sistema de eventos contém muitos polyfills, que, para muitos navegadores novos, não são necessários, e a equipe de desenvolvimento está considerando opções para removê-los ou simplificá-los, se possível. Você pode assistir aqui .

Posso medir o nível atual de desempenho do site?


Um aplicativo React típico pode conter muitos componentes e bibliotecas de terceiros. Isso significa que o desempenho do aplicativo "Olá, Mundo" não nos fornece informações particularmente valiosas sobre como aplicativos reais são carregados. Existe uma maneira de descobrir qual é o alto desempenho da maioria dos sites criados usando alguma tecnologia (como React)?

O recurso de arquivamento HTTP pode nos ajudar a responder a esta pergunta. Esta é uma plataforma de código aberto que se concentra em observar como a Web é construída. Isso é feito rastreando mensalmente milhões de sites, analisando-os usando o WebPageTest e registrando informações sobre eles. Essas informações incluem o número de consultas, métricas relacionadas ao carregamento de dados, tamanhos de dados transmitidos e outros indicadores.

Aqui está outra ferramenta interessante - uma extensão para o Chrome chamada Library-Detector-for-Chrome . Ele permite que você descubra quais bibliotecas JavaScript são usadas na página. Foi recentemente incluído como uma ferramenta de auditoria de páginas no Lighthouse. Isso sugere que as informações fornecidas por esta extensão podem ser obtidas para muitos sites cujas informações são armazenadas no arquivo HTTP. Isso pode ajudar aqueles que desejam analisar os resultados dos testes de milhares de sites que usam uma biblioteca JavaScript específica (o mecanismo para determinar o React está aqui ).

O conjunto de dados completo do arquivo HTTP está disponível ao público e pode ser encontrado no BigQuery . Depois de pesquisar 140.000 sites usando o React para carregá-los em um ambiente móvel simulado artificialmente (conjunto de dados 2019_01_01 ), conseguimos descobrir o seguinte:

  • A mediana do indicador Primeira pintura significativa (primeira exibição significativa, tempo para desenhar elementos importantes) foi de 6,9 ​​s.
  • A mediana do indicador Time to Interactive (tempo para interatividade, tempo de carregamento dos elementos de interação) foi de 19,7 s.

Você pode explorar esses dados você mesmo.

Leva quase 20 segundos para o usuário começar a interagir com o site. E isso, em geral, está acontecendo, por assim dizer, aqui e agora, embora possa parecer implausível. Isso pode ser visto em sites grandes ao trabalhar com eles a partir de dispositivos fracos em linhas de comunicação lentas. Além disso, agora gostaria de expressar várias razões pelas quais esses dados devem ser considerados com certo grau de ceticismo.

  • Existem muitos fatores que influenciam o desempenho do site. Entre eles - a quantidade de código JavaScript enviado ao usuário, o número de imagens e outros materiais exibidos na página e assim por diante. Ele comparará incorretamente o desempenho de qualquer site baseado no React com o desempenho de uma página chamada "Olá, Mundo" se outros fatores não forem levados em consideração.
  • Se você tentar obter os dados substituindo o nome de alguma outra biblioteca na solicitação React, você obterá números muito semelhantes.

Para obter dados precisos sobre que tipo de desempenho os sites existentes realisticamente usando uma determinada biblioteca demonstram, muito trabalho precisa ser feito.

Crescimento do código JavaScript


O problema comum dos sites modernos, não vinculado a uma biblioteca específica, está relacionado à quantidade de código JavaScript que um cliente geralmente precisa carregar ao navegar. O arquivo HTTP já tem uma boa conta disso. Em resumo, eis a aparência dos valores medianos dos volumes JavaScript baixados de sites em anos diferentes:

  • 74,7 Kb - páginas da web para celular, 15 de dezembro de 2011.
  • 384,4 Kb - páginas da web para celular, 15 de dezembro de 2018.

Lembre-se de que esses dados foram obtidos após o processamento de milhões de páginas. Provavelmente existem milhares de sites atípicos que distorcem esse indicador. Esta é uma ideia viável. Portanto, tentaremos descobrir como esse indicador fica para os primeiros 10.000 sites do ranking Alexa:

  • 381,5 Kb - páginas da web para celular, 15 de dezembro de 2018 (eis a solicitação ).

Tudo isso nos permite concluir que atualmente estão sendo criados sites que incluem mais código JS do que sites criados há vários anos. Esta é uma observação importante. Os sites se tornaram maiores, se tornaram mais interativos e mais complexos, e o volume do código JS desses sites está aumentando gradualmente a cada ano. Você provavelmente já ouviu falar sobre isso, mas quanto mais código JavaScript você envia ao navegador, mais tempo leva para analisá-lo, compilá-lo e executá-lo. Como resultado, isso diminui a velocidade do site.

É importante observar que cada site é único, assim como a base de usuários de cada site. Muitos desenvolvedores, cujos sites incluem mais de 300 KB de código JS, não enfrentam o fato de que a maioria de seus usuários sofre de problemas de desempenho, e isso é completamente normal. No entanto, se você estiver preocupado com a possibilidade de os usuários poderem visualizar o site do React, para avaliar a situação real, é melhor começar com a criação de perfil.

Perfil e análise de página


A criação de perfil e a análise de aplicativos React podem ser visualizadas de duas perspectivas:

  • Em primeiro lugar, trata-se de avaliar o desempenho dos componentes. Isso afeta a maneira como os usuários interagem com o site. Por exemplo, se uma lista for exibida clicando em um botão, isso deve ser feito rapidamente, mas se centenas de componentes forem renderizados novamente durante esta operação, mesmo que não seja necessário, essa operação será percebida como lenta.
  • Em segundo lugar, estamos falando de quanto tempo o aplicativo é colocado em condições de trabalho. Ou seja, quanto tempo após o início do carregamento do site, o usuário poderá interagir com ele. A quantidade de código enviada ao usuário durante o carregamento da primeira página do site é um exemplo de um fator que afeta a rapidez com que o usuário pode começar a trabalhar com o aplicativo.

Avaliação de desempenho e otimização de componentes


Vamos tentar expressar em uma frase o significado do algoritmo de reconciliação React ou a essência do que é chamado de "DOM virtual". Ele terá a seguinte aparência: "O React executa etapas para distinguir entre a nova árvore DOM e a árvore antiga, a fim de entender o que exatamente na interface do usuário deve ser atualizado quando os dados no componente forem alterados". Isso cria uma carga muito menor no sistema do que renderizar novamente o aplicativo inteiro para cada alteração de estado ou propriedades ( aqui você pode ler sobre a diferença entre O (n 3 ) e O (n)). Aqui está um artigo de Dan Abramov, onde você pode encontrar explicações sobre reconciliação.

Mesmo levando em consideração o fato de que essas otimizações são incorporadas aos mecanismos internos do React, sempre é possível encontrar um problema quando os componentes no aplicativo são renderizados repetidamente quando isso não deve acontecer. Em aplicativos pequenos, isso pode não ser perceptível, mas pode afetar seriamente o desempenho de aplicativos que exibem centenas de componentes em suas páginas.

A renderização desnecessária dos componentes é feita por vários motivos. Por exemplo, funções que funcionam dentro de componentes podem não ser tão eficazes quanto podem ser, ou talvez uma lista inteira de componentes seja redesenhada quando apenas um novo elemento é adicionado a essa lista. Existem ferramentas que você pode usar para descobrir quais árvores de componentes foram renderizadas por muito tempo. Entre eles, destacam-se:

  • O Painel de Ferramentas do Desenvolvedor do Chrome.
  • React Developer Tool Profiler.

▍ Análise de desempenho usando o painel de desempenho das Ferramentas do desenvolvedor do Chrome


O React usa a API de tempo do usuário para medir o tempo gasto em cada etapa do ciclo de vida do componente. As informações de desempenho dos aplicativos React podem ser coletadas e analisadas usando as Ferramentas do desenvolvedor do Chrome. Isso permite que você entenda com que eficácia os componentes são conectados, exibidos na página e desconectados durante a interação do usuário com a página ou quando ela é recarregada.


Análise de desempenho de componentes

Aqui está um bom material sobre esse tópico, dedicado à pesquisa do desempenho de aplicativos escritos usando o React 16 usando as ferramentas de desenvolvedor do Chrome.

A API de tempo do usuário é usada apenas durante o desenvolvimento. Em produção, está desligado. Em tais condições, implementações mais rápidas de tais mecanismos podem ser usadas, sem causar um sério impacto no desempenho. Foi a necessidade de tais mecanismos que se tornou um dos motivos do desenvolvimento do API Profiler mais recente.

▍Análise do desempenho usando o criador de perfil das ferramentas do desenvolvedor do React


Com o lançamento da biblioteca react-dom 16.5 nas ferramentas de desenvolvedor do React, um novo painel chamado Profiler pode ser usado. Permite analisar o desempenho da renderização de componentes. Isso é feito usando a API Profiler, que coleta informações sobre o tempo de execução das operações para todos os componentes que são renderizados novamente.

O painel Profiler é uma guia independente nas ferramentas do desenvolvedor do React. Aqui, como no painel Desempenho da barra de ferramentas Ferramentas do desenvolvedor do Chrome, você pode registrar informações sobre ações do usuário e recarregamentos de páginas para coletar dados para analisar o desempenho dos componentes.


Coletando Dados com o React Developer Tools

Após a conclusão da coleta de dados, o chamado “gráfico de fogo” será exibido, mostrando o tempo necessário para renderizar os componentes na página.


Programação do Flaming Profiler

Aqui você pode alternar entre diferentes confirmações ou estados quando os nós do DOM foram adicionados, excluídos ou atualizados. Isso permite que você obtenha mais informações sobre quanto tempo é gasto em várias operações com componentes.


Exibir informações de confirmação no criador de perfil

As capturas de tela apresentadas aqui representam os dados obtidos como resultado da gravação de ações do usuário executadas em um aplicativo simples. O aplicativo baixa uma lista dos repositórios mais populares do GitHub quando um botão é clicado. Como você pode ver, existem apenas dois commits aqui:

  • Uma é para o indicador de carregamento, que é exibido durante o carregamento da lista de itens.
  • Outro representa o momento em que a chamada para a API é concluída e a lista é exibida no DOM.

A figura à direita mostra metadados úteis que incluem informações de confirmação ou dados de componentes, como propriedades e estado.


Metadados

Ao trabalhar com o criador de perfil do React, você também pode visualizar outros dados representados por vários gráficos. Você pode ler mais sobre o gerador de perfil do React nesta postagem no blog do React.

Para complicar um pouco o nosso exemplo, considere uma situação semelhante, mas agora faremos muitas chamadas de API para baixar repositórios de tendências em diferentes linguagens de programação (como Golang, JavaScript e assim por diante). Como você pode esperar, com essa abordagem, temos mais confirmações à nossa disposição.


Aumentar o número de confirmações enquanto complica o esquema de trabalhar com o aplicativo

As confirmações posteriores são diferenciadas por agendas mais longas, têm mais cor amarela. Isso significa que o tempo necessário para que todos os componentes concluam a renderização aumenta à medida que o tamanho da lista de elementos na página aumenta. Isso se deve ao fato de que cada componente da lista sofre nova renderização a cada nova chamada para a API. Isso ajuda a identificar um problema que pode ser facilmente resolvido. A solução é que os itens da lista não precisam ser renderizados novamente quando novos itens forem adicionados à lista.

▍ Minimizar operações desnecessárias de re-renderização de componentes


Existem muitas maneiras de se livrar de operações desnecessárias para renderizar novamente os componentes do React, ou pelo menos para minimizar o número dessas operações. Vamos considerar alguns deles.

  • Você pode usar o método do ciclo de vida do componente shouldComponentUpdate () :

     shouldComponentUpdate(nextProps, nextState) { // true        } 
  • Você pode usar o PureComponent para construir componentes baseados em classe:

     import React, { PureComponent } from 'react'; class AvatarComponent extends PureComponent { } 
  • Para componentes funcionais, você pode usar o memorando :

     import React, { memo } from 'react'; const AvatarComponent = memo(props => { }); 
  • Você pode memorizar seletores Redux (por exemplo, usando a seleção novamente ).
  • Você pode otimizar a saída de listas muito longas usando a virtualização (por exemplo, usando a janela de reação ).

Aqui e ali - alguns vídeos úteis, que discutem o uso do profiler React para encontrar gargalos nos aplicativos.

Avaliação de desempenho e otimização de aplicativos


Além de analisar as mutações do DOM e as operações de renderização de componentes, há outros fenômenos de nível superior que vale a pena explorar. Para uma avaliação abrangente do desempenho do site, você pode usar o Lighthouse .

Existem três maneiras de testar uma página da web usando o Lighthouse:

  • Usando a interface da linha de comandos Node.js.
  • Usando a extensão do Chrome.
  • Use a barra de ferramentas Auditorias das Ferramentas do desenvolvedor do Chrome.

Veja como o Lighthouse se parece no painel Auditorias.


Farol no painel Auditorias

O Lighthouse geralmente não requer muito tempo para coletar todos os dados de que ele precisa da página e para executar muitas verificações desses dados. Após a conclusão dessas operações, o Lighthouse exibe um relatório com informações resumidas.

Para entender que carregar uma página no navegador envolve carregar muito código JavaScript e concluir que a quantidade desse código deve ser reduzida, preste atenção às seguintes frases do relatório:

  • Eliminar recursos de bloqueio de renderização
  • O tempo de inicialização do JavaScript é muito alto
  • Evite enormes cargas de rede

Se o Lighthouse relatar esses problemas porque a página usa um pacote JS muito grande, a primeira coisa que vale a pena considerar como uma maneira de corrigir o problema é dividir o pacote. O fato é que, se o código puder ser dividido em fragmentos, alguns dos quais são necessários apenas para trabalhar com determinadas páginas do site, não teremos motivos para não usar essa oportunidade.

Separation Separação de pacote JS


Uma maneira de dividir o código em partes é usar importações dinâmicas:

   import('lodash.sortby')   .then(module => module.default)   .then(module => doSomethingCool(module)) 

A sintaxe de importação pode parecer uma chamada de função, mas permite importar qualquer módulo de forma assíncrona usando o mecanismo promises. Neste exemplo, o método sortby é primeiro importado da biblioteca lodash e, em seguida, o método doSomethingCool() é doSomethingCool() .


Pedido de classificação de números

Neste exemplo, acontece o seguinte:

  1. O usuário clica em um botão para classificar os três números.
  2. Importado lodash.sortby .
  3. O método doSomethingCool() é doSomethingCool() , que classifica os números e os exibe no novo nó do DOM.

Este é um exemplo extremamente simples e artificial, porque se alguém precisar classificar números em uma página da Web, provavelmente usará o método Array.prototype.sort() . , , .

, JavaScript TC39. Chrome Safari , Webpack , Rollup Parcel .
React, , . — React.lazy :

 import React, { lazy } from 'react'; const AvatarComponent = lazy(() => import('./AvatarComponent')); 

, , , . Suspense , , «» . React.lazy , , , :

 import React, { lazy, Suspense } from 'react'; import LoadingComponent from './LoadingComponent'; const AvatarComponent = lazy(() => import('./AvatarComponent')); const PageComponent = () => ( <Suspense fallback={LoadingComponent}>   <SomeComponent /> </Suspense> ) 

Suspense . React-, , , , React, loadable-components .

 import React from 'react'; import loadable from '@loadable/component' import LoadingComponent from './LoadingComponent'; const AvatarComponent = loadable(() => import('./AvatarComponent'), {   LoadingComponent: () => LoadingComponent }); 

LoadingComponent loadable-component .

, , loadable-components , - .

, . , , . React , React Suspense .

▍ , ?


, react-loadable-visibility , loadable-components -API Intersection Observer. , .

 import React from 'react'; import loadableVisibility from 'react-loadable-visibility/loadable-components' const AvatarComponent = loadableVisibility(() => import('./AvatarComponent'), { LoadingComponent: Loading, }) 

▍ ,


- — -, . - , , . , , , -, -, , .





Workbox
— , -, . CRA 2.0 , , src/index.js . - .

   import React from 'react'; //... //   ,          //    ,     // unregister()  register().    ,     - //    . //     : http://bit.ly/CRA-PWA serviceWorker.unregister(); 

- Workbox .


- HTML-, , , . , , , , , JS-, .

, , DOM-, , , (, hydrate() React). , , , , .

React 16, . renderToString() HTML-, renderToNodeStream() Readable- Node.

HTML- , . , , .

React , , , renderToStaticNodeStream .

▍ ,


- , , , , - , , . , , .

, , , . HTML- , .

, react-snap , Puppeteer .

, .

▍ , CSS-in-JS


React-, , CSS-in-JS- emotion styled-components . , , , , , . CSS-in-JS , , , . , , JavaScript- , , . , , , .


, ( )

- , . emotion styled-components . Readable- Node. Glamor , .

, .


Preact-, ( )

, CSS-in-JS — , , , , , astroturf . , , , , .


«», , . , , .

, Lighthouse. , React-, React A11y .

, react-axe , , JSX-.


? -, , ? , ?

, - . «» . , — , .

create-react-app -:

 { "short_name": "React App", "name": "Create React App Sample", "icons": [   {     "src": "favicon.ico",     "sizes": "64x64 32x32 24x24 16x16",     "type": "image/x-icon"   } ], "start_url": ".", "display": "standalone", "theme_color": "#000000", "background_color": "#ffffff" } 


, . , , . — . Por que isso é assim? , , .

▍Atomic CSS


CSS- (Atomic CSS) , . , , :

 <button class="bg-blue">Click Me</button> 

padding :

 <button class="bg-blue pa2">Click Me</button> 

E assim por diante class DOM, , , CSS-, . , . Tachyons .

. tachyon-components API, API styled-components :

 import styled from 'tachyons-components' const Button = styled('button')` bg-blue pa2 ` <Button>Click Me</Button> 

, , CSS- «» , CSS-, , .


, , . — :

 import { useState } from 'react'; function AvatarComponent() { const [name, setName] = useState('Houssein'); return (   <React.Fragment>     <div>       <p>This is a picture of {name}</p>       <img align="center" src="avatar.png" />     </div>     <button onClick={() => setName('a banana')}>       Fix name     </button>   </React.Fragment> ); } 

:


, . , , recompose .

React Conf, .

, , JavaScript- . , React , 1.5 ( ). , - , , , , , , , .

Sumário


, React-. , , :

  1. , :

    • Performance Chrome.
    • React.


    • , , shouldComponentUpdate() .
    • , , PureComponent .
    • React.memo .
    • Redux (, reselect ).
    • (, react-window ).
  2. Lighthouse.
  3. , :

    • React.lazy .
    • loadable-components .
    • - , . Workbox .
    • — ( renderToNodeStream renderToStaticNodeStream ).
    • ? react-snap .
    • , CSS-in-JS.
    • , . React A11y react-axe .
    • - , , , .

, React, , :

  • API, , .
  • , , , .

« » . -, .

Caros leitores! React-?

Source: https://habr.com/ru/post/pt442650/


All Articles