Reduzindo tamanhos de pacotes com o Webpack Analyzer e React Lazy / Suspense

À medida que a complexidade dos aplicativos clientes aumenta, o tamanho de seus pacotes configuráveis ​​aumenta cada vez mais. Nessa situação, as pessoas sofrem mais, forçadas, por várias razões, a usar conexões lentas com a Internet. Além disso, todos os dias só piora.



O autor do artigo, cuja tradução publicamos hoje, trabalha no Wix. Ele quer falar sobre como conseguiu reduzir o tamanho de um pacote em cerca de 80%, usando o Webpack Analyzer e o React Lazy / Suspense.

Qual a antecipação da otimização?


Se você acabou de começar a trabalhar em seu novo aplicativo Web, provavelmente está tentando se concentrar em fazê-lo, por assim dizer, "decolar", tentando fazê-lo funcionar. Você provavelmente não presta muita atenção ao desempenho ou no tamanho do pacote. Eu entendo isso. No entanto, minha experiência sugere que o desempenho e o tamanho dos pacotes devem ser resolvidos desde o início. Uma boa arquitetura de aplicativos e uma oportuna “reflexão sobre o futuro do projeto” economizarão, a longo prazo, muito tempo e ajudarão a não acumular sérias dívidas técnicas. Obviamente, é difícil "prever" tudo com antecedência, mas você deve se esforçar ao máximo para fazer tudo certo desde o primeiro dia de trabalho no projeto.

Aqui estão algumas ótimas ferramentas que eu acho que devem ser usadas desde o início. Essas ferramentas ajudarão você a reconhecer pacotes NPM "problemáticos" mesmo antes de ocuparem qualquer lugar importante no aplicativo.

UndBundlefobia


Bundlephobia é um site que permite saber quanto um determinado pacote NPM aumentará o tamanho de um pacote. Essa é uma ótima ferramenta para ajudar o programador a tomar as decisões corretas em relação à escolha de pacotes de terceiros que ele possa precisar. A bundlephobia ajuda a projetar a arquitetura do aplicativo para que seu tamanho não seja muito grande. A figura a seguir mostra os resultados da verificação de uma biblioteca popular para trabalhar com o tempo, chamada momento. Você pode ver que essa biblioteca é bastante grande - quase 66 Kb na forma compactada. Para muitos usuários que trabalham com Internet de alta velocidade, isso não é nada. No entanto, vale a pena prestar atenção no que se torna o tempo de download deste pacote nas redes 2G / 3G. São respectivamente 2,2 e 1,32 segundos. E, preste atenção, estamos falando apenas deste pacote.


Resultados da análise de pacotes de momentos Bundlephobia

CostCusto de importação


O Custo de importação é uma extensão muito interessante para muitos editores de código populares (possui mais de um milhão de downloads para o VS Code ). Pode mostrar o "custo" dos pacotes importados. Eu gosto especialmente porque ajuda a identificar áreas problemáticas do aplicativo corretamente enquanto trabalha nele. A figura a seguir (retirada da página GitHub de custo de importação) mostra um excelente exemplo do impacto nas dimensões do projeto de uma abordagem diferente para as entidades importadoras. Portanto, importar a única propriedade uniqueId do Lodash leva à importação de toda a biblioteca para o projeto (70 Kb). E se você importar diretamente a função uniqueId , apenas 2 Kb serão adicionados ao tamanho do projeto. Leia mais sobre o custo de importação aqui .


O "custo" de importar toda a biblioteca Lodash para o projeto e apenas uma função específica dessa biblioteca

O caso de pacotes excessivamente grandes


Então, você criou seu aplicativo maravilhoso. Funciona muito bem na Internet de alta velocidade e no computador mais poderoso, cheio de RAM. Você lançou no mundo real. Depois de algum tempo, começaram a surgir reclamações suas ou de seus próprios analistas. Essas reclamações estavam relacionadas aos tempos de carregamento do aplicativo. Algo semelhante aconteceu recentemente comigo quando nós, no Wix, revelamos um novo recurso em que eu estava trabalhando.

Para acelerar um pouco, vamos falar sobre esta nova oportunidade. Essa é uma nova barra de progresso localizada na parte superior do painel lateral da interface de configurações do site do usuário. O objetivo desse mecanismo é chamar a atenção do usuário para as várias etapas que ele precisa executar para que seu projeto de negócios tenha uma melhor chance de sucesso (conexão com o SEO, adição de regiões nas quais os produtos são entregues, adição do primeiro produto) e assim por diante).

A barra de progresso é atualizada automaticamente conectando-se ao servidor usando soquetes da web. Quando o usuário conclui todas as etapas recomendadas, uma janela pop-up é exibida com parabéns. Depois que essa janela é fechada, a barra de progresso fica oculta e nunca é exibida novamente ao trabalhar com o site em que você a usou. É disso que acabamos de falar.


Barra de progresso e janela de parabéns

O que aconteceu? Por que nossos analistas se queixaram de que o tempo de carregamento da página aumentou? Quando examinei o estado das coisas usando a guia Rede das ferramentas de desenvolvedor do Chrome, imediatamente ficou claro para mim que meu pacote era muito grande. Ou seja, seu tamanho era de 190 Kb.


Tamanho do pacote encontrado usando as Ferramentas do desenvolvedor do Chrome

"Por que essa coisinha precisa de um pacote relativamente grande?", Pensei. Mas a verdade - por quê?

▍Procure pontos problemáticos no pacote


Depois que percebi que o tamanho do pacote é muito grande, é hora de descobrir o motivo disso. É aqui que o Webpack Bundle Analyzer foi útil - uma ótima ferramenta para identificar áreas problemáticas de pacotes configuráveis . Ele abre uma nova guia do navegador e exibe informações de dependência.

Foi o que aconteceu depois que analisei o pacote com esta ferramenta.


Resultados do Webpack Bundle Analyzer

Com a ajuda do analisador, consegui detectar o "criminoso". O pacote lottie-web foi usado aqui, que adicionou 61,45 Kb ao tamanho do pacote. Lottie é uma biblioteca JavaScript muito boa que permite o uso de ferramentas padrão do navegador para gerar animações criadas no Adobe After Effect. No meu caso, foi para que nosso designer precisasse de uma boa animação, que foi realizada quando a janela de parabéns apareceu. Ele criou essa animação e me deu na forma de um arquivo JSON, que eu transferi para o pacote Lottie e recebi uma bela animação. Além do pacote lottie-web, eu também tinha um arquivo JSON com descrições de animação. O tamanho deste arquivo era 26 Kb. Como resultado, a biblioteca Lottie, o arquivo JSON e também algumas pequenas dependências auxiliares “me custam” aproximadamente 94 Kb. E isso é apenas uma animação da janela com parabéns ao usuário. O usuário, quando viu esses parabéns, deveria estar feliz. Tudo isso me deixou triste.

A tecnologia React Lazy / Suspense vem em socorro


Depois que eu descobri a causa do problema, chegou a hora de resolvê-lo. Ficou claro que não era necessário carregar, no início do trabalho, tudo o que era necessário para a animação. De fato, havia uma chance considerável de que, durante a sessão atual do usuário, ele não tivesse que mostrar uma janela com parabéns. Então me familiarizei com as tecnologias React Lazy / Suspense, recentemente exibidas, e pensei que agora provavelmente tinha uma boa oportunidade de testá-las.

Se você não estiver familiarizado com o conceito de componentes "preguiçosos" (preguiçosos), saiba que o significado deles é dividir o aplicativo em pequenos pedaços de código. O download desses fragmentos é realizado apenas quando necessário. No meu caso, isso foi expresso no fato de que eu precisava selecionar entre as principais funções funcionais da barra de progresso o componente responsável por mostrar os parabéns. Foi necessário carregar esse componente apenas quando o usuário concluiu a sequência recomendada de etapas.

O React 16.6.0 (e versões mais recentes) possui uma API simples projetada para renderizar componentes preguiçosos. Estes são React.lazy e React.Suspense . Considere um exemplo:

 const OtherComponent = React.lazy(() => import('./OtherComponent')); function MyComponent() {  return (    <div>      <React.Suspense fallback={<div>Loading...</div>}>        <OtherComponent />      </React.Suspense>    </div>  ); } 

Temos um componente que exibe o elemento <div> e nele existe um componente Suspense que envolve o componente OtherComponent . Se você observar a primeira linha deste exemplo, poderá ver que OtherComponent não OtherComponent importado diretamente para o código. Normalmente, uma importação como essa se parece com import OtherComponent from './OtherComponent'; .

Em vez disso, o comando import é estruturado como uma função que utiliza um caminho de arquivo. Esse mecanismo funciona porque o Webpack possui ferramentas de separação de código integradas. Quando uma construção semelhante está presente no código, é retornada uma promessa que, após o download do arquivo, é resolvida com o conteúdo desse arquivo. Nossa equipe de importação está envolvida em uma função React.lazy .

Nos materiais de renderização retornados por MyComponent , OtherComponent envolvido em um componente React.Suspense que possui uma propriedade de fallback . No nosso caso, acontece que quando a renderização atinge OtherComponent , o carregamento do componente correspondente é iniciado. Enquanto isso, o que é gravado na propriedade fallback é renderizado. Neste exemplo, este é o texto Loading… Isso, de fato, é tudo. Esses mecanismos apenas fazem seu trabalho.

É verdade que existem alguns recursos que você precisa considerar ao trabalhar com o Lazy / Suspense.

  1. Um componente importado de forma "lenta" deve conter a exportação padrão, que será o ponto de entrada do componente. As exportações nomeadas não podem ser usadas aqui.
  2. Você precisa React.lazy o componente importado usando a função React.lazy no componente React.Suspense . O componente React.Suspense fornecer a propriedade fallback . Caso contrário, ocorrerá um erro. No entanto, se você simplesmente não deseja renderizar nada até que o componente termine o carregamento lento, basta escrever null no fallback sem tentar contornar a necessidade de escrever algo nesta propriedade de alguma maneira complicada.

O uso do React Lazy / Suspense me ajudou?


Sim, isso ajudou! A divisão de código funcionou de maneira surpreendente. Vamos dar uma olhada nos resultados da análise do novo pacote usando o Webpack.


Resultados do Webpack Bundle Analyzer após divisão de código

Como você pode ver, o tamanho do meu pacote diminuiu cerca de 50% - para 96 ​​Kb. Ótimo!

E agora, o problema está resolvido? Infelizmente não. Quando olhei para a página, constatou-se que o pop-up com parabéns perdeu o posicionamento.


A janela de parabéns não aparece onde você precisa

O problema foi que eu "pedi" a janela para abrir, alterando o estado do componente React. Enquanto isso, já processei null (ou seja, não React.Suspense nada) usando o componente React.Suspense . Após o carregamento lento dos dados necessários, os materiais relevantes foram adicionados ao DOM. No entanto, o posicionamento do pop-up já foi feito. Como resultado, devido ao fato de as propriedades do componente correspondente não terem sido alteradas, esse componente “não sabia” que precisava resolver o problema referente ao posicionamento. Se eu alterasse o tamanho da janela do navegador, a janela pop-up aparecia no lugar certo devido ao fato de o componente correspondente estar observando alterações nas propriedades e eventos de redimensionamento da janela, iniciando, se necessário, reposicionando.

Como resolver este problema? A solução foi eliminar o "intermediário".

Eu tive que carregar primeiro o componente "preguiçoso" e só depois escrever para o estado que informaria a janela com os parabéns que ele precisa abrir. Consegui fazer isso usando os mesmos mecanismos de compartilhamento de código do Webpack, mas agora - sem implementar importações usando o React.lazy :

 async loadAndSetHappyMoment() {  const component = await import(    '../SidebarHappyMoment/SidebarHappyMoment.component'  );  this.SidebarHappyMoment = component.SidebarHappyMoment;  this.setState({    tooltipLevel: TooltipLevel.happyMoment,  }); } 

Essa função é chamada depois que meu componente, por meio do mecanismo de soquetes da web, é informado de que precisa mostrar uma janela com parabéns. Eu usei a função de import do Webpack (segunda linha de código). Se você se lembra, eu disse acima que esta função retorna uma promessa, como resultado, eu pude usar a construção async/await .

Após o carregamento do componente, escrevo-o na instância do meu componente (com o comando this.SidebarHappyMoment = component.SidebarHappyMoment; ). Isso me dá a oportunidade de usá-lo mais tarde ao renderizar. Observe que agora posso usar exportações nomeadas. No meu caso, usei, na linha acima, o nome SidebarHappyMoment . E, finalmente, e isso não é menos importante do que tudo o resto, "informo" a janela que ele precisa abrir, alterando seu estado de acordo depois que eu souber que o componente está pronto para operação.

Como resultado, o código de renderização agora fica assim:

 renderTooltip() {  if (this.state.tooltipLevel === TooltipLevel.happyMoment) {    return <this.SidebarHappyMoment />;  }  // ... } 

Observe que o comando return <this.SidebarHappyMoment />; retorna this.SidebarHappyMoment - o que escrevi anteriormente na instância do meu componente. Agora, essa é uma função de renderização síncrona normal, a mesma que você usou um milhão de vezes. E agora a janela com parabéns é exibida exatamente onde deve ser exibida. E o fato é que ele é aberto somente após o conteúdo estar pronto para uso.

Produto define arquitetura


Se a idéia no cabeçalho desta seção despertou um grande ponto de interrogação em sua imaginação, lembre-se de que esse é o caso. O produto define a arquitetura.

É sobre o fato de que o produto define o que deve ser visível e interativo quando o componente é renderizado pela primeira vez. Isso ajuda o desenvolvedor a descobrir o que exatamente ele pode separar do código principal e carregar mais tarde, quando necessário. Pensei na situação e lembrei-me de que, depois que o usuário conclui as etapas recomendadas para configurar o site, ou se ele não é o administrador do site, não preciso mostrar a barra de progresso e o que há com ela conectado. Agora, usando essas informações, eu pude continuar compartilhando o pacote. Foi o que eu consegui.


A separação contínua do pacote

Depois disso, o tamanho do pacote foi de apenas 38 Kb. E lembro que começamos com 190 Kb. Há uma redução no tamanho do pacote em 80%. E, a propósito, já vejo outras possibilidades de separação de código. Mal posso esperar para continuar otimizando o pacote.

Sumário


Os desenvolvedores têm a capacidade de se esforçar para permanecer em sua "zona de conforto" e não se aprofundar em nada além do dispositivo do código em si e de sua funcionalidade. No entanto, um programador que usa as ferramentas descritas acima, pensa de forma criativa e trabalha em estreita colaboração com outros especialistas, pode melhorar o desempenho de seu aplicativo reduzindo significativamente o tamanho do pacote que contém o necessário para começar a trabalhar com esse aplicativo.

Caros leitores! Você usa a divisão de código para acelerar o carregamento de seus aplicativos da web?


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


All Articles