À 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 BundlephobiaCostCusto 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 bibliotecaO 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énsO 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 AnalyzerCom 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.
- 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.
- 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ódigoComo 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ê precisaO 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 pacoteDepois 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?
