
Divisão de código. A divisão de código está em todo lugar. No entanto, por que? Só porque hoje existe muito javascript e nem todos estão em uso no mesmo momento.
JS é uma coisa muito pesada . Não para o seu iPhone Xs ou para o novo laptop i9, mas para milhões (provavelmente bilhões) de proprietários de dispositivos mais lentos . Ou, pelo menos, para seus relógios.
Então - o JS é ruim, mas o que aconteceria se o desativássemos - o problema desapareceria ... para alguns sites, e desapareceria "com sites" para os baseados no React. Mas de qualquer maneira - existem sites que podem funcionar sem JS ... e há algo que devemos aprender com eles ...
Divisão de código
Hoje temos dois caminhos a percorrer, duas maneiras de melhorar ou não piorar:
1. Escreva menos código
Essa é a melhor coisa que você pode fazer. Embora o React Hooks
permita que você envie um pouco menos de código, e soluções como o Svelte
permitem gerar apenas menos código do que o normal , isso não é tão fácil de fazer.
Não se trata apenas do código, mas também da funcionalidade - para manter o código "compacto", você precisa mantê-lo "compacto". Não há como manter pequeno o pacote de aplicativos se ele estiver fazendo tantas coisas (e for enviado em 20 idiomas).
Existem maneiras de escrever códigos curtos e sonoros e de escrever a implementação oposta - a sangrenta empresa . E, você sabe, ambos são legítimos.

Mas a questão principal - o próprio código. Um aplicativo de reação simples pode facilmente ignorar 250kb "recomendado". E você pode passar um mês otimizando e diminuindo. Otimizações "pequenas" são bem documentadas e bastante úteis - basta obter o bundle-analyzer
com size-limit
e voltar à forma.
Existem muitas bibliotecas, que lutam por todos os bytes, tentando mantê-lo em seus limites - pré - ação e armazenamento , para citar algumas.
Mas nossa aplicação está um pouco além dos 200kb. É mais perto de 100Mb . Remover kilobytes não faz sentido. Mesmo remover megabytes não faz sentido.
Depois de um momento, é impossível manter seu aplicativo pequeno. Crescerá com o tempo.
2. Envie menos código
Como alternativa, o code split
. Em outras palavras - render - se . Pegue o seu pacote de 100mb e faça vinte pacotes de 5mb. Honestamente - essa é a única maneira possível de lidar com seu aplicativo, se ele for grande - crie um pacote de aplicativos menores a partir dele.
Mas há uma coisa que você deve saber agora: seja qual for a opção escolhida, é um detalhe de implementação, enquanto procuramos algo mais confiável.
A verdade sobre a divisão de código
A verdade sobre a divisão de código é que sua natureza é separação do tempo. Você não está apenas dividindo seu código, mas de uma maneira que utilizará o mínimo possível em um único momento.
Apenas não envie o código que você não precisa no momento. Livre-se disso.

Fácil de dizer, difícil de fazer. Tenho alguns aplicativos pesados, mas não adequadamente divididos, em que qualquer página carrega 50% de tudo. Às vezes code splitting
torna uma code separation
, quero dizer - você pode mover o código para os diferentes blocos, mas ainda assim, usar tudo. Lembre-se de que "simplesmente não envie o código que você não precisa no momento" , - eu precisava de 50% do código, e esse era o problema real.
Às vezes, basta adicionar a import
aqui e não é suficiente. Até que não seja a separação do tempo , mas apenas a separação do espaço - isso não importa.
Existem três maneiras comuns de dividir o código:
- Apenas
import
dinâmica. Mal usado sozinho hoje em dia. É mais sobre problemas com o rastreamento de um estado . Lazy
Component, quando você pode adiar a renderização e o carregamento de um React Component. Provavelmente 90% dos "códigos de reação dividem" atualmente.Library
Preguiçosa , que na verdade é .1
, mas você receberá um código de biblioteca por meio de objetos de renderização do React. Implementado em componentes importados de reação e componentes carregáveis . Bastante útil, mas não bem conhecido.
Divisão de código no nível do componente
Este é o mais popular. Como uma divisão de código por rota ou divisão de código por componente. Não é tão fácil fazê-lo e, como resultado, mantém bons resultados perceptivos . É a morte do Flash of Loading Content
.
As boas técnicas são:
- carrega
js chunk
e data
para uma rota em paralelo. - use um
skeleton
para exibir algo semelhante à página antes do carregamento da página (como o Facebook). prefetch
pedaços, você pode até usar palpites-js para obter uma previsão melhor.- use alguns atrasos, indicadores de carregamento,
animations
e Suspense
(no futuro) para suavizar as transições.
E, você sabe, isso é tudo sobre desempenho perceptivo .

Imagem do UX aprimorado com elementos do Ghost
Isso não parece bom
Sabe, eu poderia me chamar de especialista em divisão de código - mas tenho minhas próprias falhas.
Às vezes, falhava em reduzir o tamanho do pacote. Às vezes, falhava em melhorar o desempenho resultante, contanto que the _more_ code splitting you are introducing - the more you spatially split your page - the more time you need to _reassemble_ your page back
*. É chamado de ondas de carregamento .
- sem SSR ou pré-renderização. O SSR adequado é um divisor de águas no momento.

Na semana passada, tenho duas falhas:
- Eu perdi em uma comparação de biblioteca , desde que minha biblioteca fosse melhor, mas MUITO maior que outra. Falha ao "1. Escreva menos código" .
- otimizar um site pequeno, feito em React por minha esposa. Ele estava usando a divisão de componentes baseada em rota, mas o
header
e o footer
foram mantidos no pacote principal para tornar as transições mais "aceitáveis". Apenas algumas coisas, firmemente acopladas entre si, com o lado do pacote disparado até 320kb (antes do gzip). Não havia nada importante e nada que eu realmente pudesse remover. Uma morte por mil cortes . Falha ao enviar menos código .
React-Dom foi de 20%, core-js foi de 10%, react-router, jsLingui, react-powerplug ... 20% de código próprio ... Já terminamos.

A solução
Comecei a pensar em como resolver meu problema e por que soluções comuns não estão funcionando corretamente no meu caso de uso.
O que eu fiz? Listei todos os locais cruciais, sem os quais o aplicativo não funcionaria, e tentei entender por que tenho o restante.
Foi uma surpresa. Mas meu problema estava no CSS. Em transição de baunilha CSS.
Aqui está o código
Gostaria de dividir esta parte do código como um todo, mas isso é algo que não pude fazer devido a dois motivos:
- as informações devem estar visíveis imediatamente, uma vez necessárias, sem demora. Um requisito comercial.
- a informação "chrome" deve existir antes, para lidar com a transição da propriedade.
Esse problema pode ser parcialmente resolvido usando CSSTransitionGroup ou recondição . Mas, você sabe, corrigir um código adicionando outro código parece estranho, mesmo que seja suficiente . Quero dizer, adicionar mais código pode ajudar a remover ainda mais código. Mas ... mas ...
Deve haver uma maneira melhor!
TL; DR - há dois pontos principais aqui:
DisplayData
deve ser montado e existe no DOM anterior.FocusLock
também deve existir antes, para não causar a DisplayData
, mas seus cérebros não são necessários no começo.
Então, vamos mudar nosso modelo mental
Batman e robin
Vamos supor que nosso código seja Batman e Robin. Batman pode lidar com a maioria dos bandidos, mas quando ele não pode, seu companheiro Robin vem em socorro ..
Mais uma vez, Batman entraria na batalha, Robin chegaria mais tarde.
Aqui é o Batman:
+<FocusLock - enabled={componentControl.value} +> - {componentControl.value && <PageTitle title={componentControl.value.title}/>} + <DisplayData + data={componentControl.value} + visible={componentControl.value !== null} + /> +</FocusLock>
Este é o seu companheiro, Robin:
-<FocusLock + enabled={componentControl.value} -> + {componentControl.value && <PageTitle title={componentControl.value.title}/>} - <DisplayData - data={componentControl.value} - visible={componentControl.value !== null} - /> -</FocusLock>
Batman e Robin poderiam formar uma EQUIPE , mas na verdade são duas pessoas diferentes.
E não se esqueça - ainda estamos falando sobre divisão de código . E, em termos de divisão de código, onde está o ajudante? Onde está Robin?

em um carro lateral. Robin está esperando em um pedaço lateral .
Sidecar
Batman
aqui é tudo visual que seu cliente deve ver o mais rápido possível. Idealmente instantaneamente.Robin
aqui é toda lógica, e recursos interativos sofisticados, que podem estar disponíveis um segundo depois, mas não no começo.
Seria melhor chamar isso de divisão de código vertical onde as ramificações de código existem em paralelo, ao contrário de uma divisão de código horizontal comum onde as ramificações de código são cortadas .
Em alguns países , esse trio era conhecido como replace reducer
ou outras maneiras de carregar preguiçosamente a lógica do redux e os efeitos colaterais.
Em alguns outros países , é conhecido como "3 Phased" code splitting
.
É apenas mais uma separação de preocupações, aplicável apenas a casos, em que é possível adiar o carregamento de parte de um componente, mas não de outra parte.

imagem de Criando o novo facebook.com com React, GraphQL e Relay , em que importForInteractions
ou importAfter
são o sidecar
- sidecar
.
E há uma observação interessante - enquanto Batman
é mais valioso para um cliente, desde que seja algo que o cliente possa ver , ele está sempre em forma ... Enquanto Robin
, você sabe, ele pode estar um pouco acima do peso e exigir muito mais bytes para vivendo.
Como resultado - o Batman sozinho é algo muito suportável para um cliente - ele fornece mais valor a um custo menor. Você é meu herói Bat!
O que pode ser movido para um carro lateral:
- maioria dos
useEffect
, componentDidMount
e amigos. - como todos os efeitos modais . Ou seja, bloqueios de
focus
e scroll
. Você pode primeiro exibir um modal e só depois torná-lo modal , ou seja, "bloquear" a atenção do cliente. - Formulários Mova toda a lógica e validações para um side-car e bloqueie o envio do formulário até que essa lógica seja carregada. O cliente pode começar a preencher o formulário, sem saber que é apenas o
Batman
. - Algumas animações. Uma
react-spring
inteira react-spring
no meu caso. - Algumas coisas visuais. Como barras de rolagem personalizadas , que podem exibir barras de rolagem sofisticadas um segundo depois.
Além disso, não se esqueça - todo código, transferido para um side-car, também transfere coisas como core-js poly- e ponyfills, usadas pelo código removido.
A Divisão de Código pode ser mais inteligente do que em nossos aplicativos hoje. Devemos perceber que existem 2 tipos de código a serem divididos: 1) aspectos visuais 2) aspectos interativos. O último pode vir alguns momentos depois. Sidecar
facilita a divisão das duas tarefas, dando a percepção de que tudo foi carregado mais rapidamente . E vai.
A maneira mais antiga de dividir código
Embora ainda não esteja claro quando e o que é um sidecar
, darei uma explicação simples:
Sidecar
é TODOS OS SEUS SCRIPTS . Sidecar é a maneira como dividimos o código antes de todas as coisas de front-end que recebemos hoje.
Eu estou falando sobre Server Side Rendering ( SSR ), ou HTML simples, todos nós estávamos acostumados ontem. Sidecar
torna as coisas mais fáceis do que costumavam ser quando as páginas continham HTML e lógica vividas separadamente em scripts externos incorporáveis (separação de preocupações).
Tínhamos HTML, além de CSS, além de alguns scripts embutidos, além do restante dos scripts extraídos para arquivos .js
.
HTML
+ CSS
+ inlined-js
eram Batman
, enquanto scripts externos eram Robin
, e o site funcionava sem Robin e, honestamente, parcialmente sem Batman (ele continuará a luta com as duas pernas (scripts embutidos) quebradas). Isso foi ontem, e muitos sites "não modernos e legais" são os mesmos hoje.
Se o seu aplicativo suportar SSR - tente desabilitar o js e fazê-lo funcionar sem ele. Então ficaria claro o que poderia ser movido para um carro lateral.
Se o seu aplicativo for um SPA somente do lado do cliente - tente imaginar como ele funcionaria, se o SSR existisse.
Por exemplo - theurge.com , escrito em React, é totalmente funcional sem nenhum js ativado .
Há muitas coisas que você pode descarregar para um carro lateral. Por exemplo:
- comentários. Você pode enviar o código para
display
comentários, mas não answer
, desde que exija mais código (incluindo o editor WYSIWYG), o que não é necessário inicialmente. É melhor atrasar uma caixa de comentários , ou mesmo ocultar o carregamento de código atrás da animação, do que atrasar uma página inteira. - player de vídeo. Envie "vídeo" sem "controles". Carregue-os um segundo depois, o cliente pode tentar interagir com ele.
- galeria de imagens, como
slick
. Não é grande coisa desenhar , mas muito mais difícil de animar e gerenciar. Está claro o que pode ser movido para um carro lateral.
Basta pensar no que é essencial para a sua aplicação e no que não é bem ...
Detalhes da implementação
(DI) Divisão do código do componente
A forma mais simples de sidecar
é fácil de implementar - basta mover tudo para um subcomponente; você pode codificar a divisão usando maneiras "antigas". É quase uma separação entre os componentes Smart e Dumb, mas desta vez o Smart não está contanitando um Dumb - é o oposto.
const SmartComponent = React.lazy( () => import('./SmartComponent')); class DumbComponent extends React.Component { render() { return ( <React.Fragment> <SmartComponent ref={this} /> // <-- move smart one inside <TheActualMarkup /> // <-- the "real" stuff is here </React.Fragment> } }
Isso também requer mover o código de inicialização para um Dumb, mas você ainda pode dividir a parte mais pesada de um código.
Você pode ver um padrão de divisão de código parallel
ou vertical
agora?
useSidecar
A criação do novo facebook.com com React, GraphQL e Relay , como já mencionei aqui, tinha um conceito de loadAfter
ou importForInteractivity
, que é bastante parecido com o conceito de sidecar.
Ao mesmo tempo, eu não recomendaria a criação de algo como useSidecar
, desde que você intencionalmente tente usar hooks
dentro, mas a divisão de código nesse formulário quebraria a regra dos ganchos .
Por favor, prefira uma forma de componente mais declarativa. E você pode usar hooks
dentro do componente SideCar
.
const Controller = React.lazy( () => import('./Controller')); const DumbComponent = () => { const ref = useRef(); const state = useState(); return ( <> <Controller componentRef={ref} state={state} /> <TheRealStuff ref={ref} state={state[0]} /> </> ) }
Pré-busca
Não se esqueça - você pode usar dicas de prioridade de carregamento para pré-carregar ou pré-buscar sidecar
e torná-lo mais transparente e invisível.
Coisas importantes - os scripts de pré-busca o carregariam via rede , mas não executam (e gastam CPU), a menos que seja realmente necessário.
SSR
Diferentemente da divisão normal do código, nenhuma ação especial é necessária para o SSR. Sidecar
pode não fazer parte do processo SSR e não ser necessário antes da etapa de hydration
. Pode ser adiado "por design".
Portanto, sinta-se à vontade para usar o React.lazy
(idealmente algo sem o Suspense
, você não precisa de nenhum indicador de failback (carregamento) aqui), ou qualquer outra biblioteca, com, mas melhor, sem o suporte do SSR para pular pedaços do sidecar durante o processo do SSR.
As partes ruins
Mas existem algumas partes ruins dessa ideia
Batman não é um nome de produção
Embora Batman
/ Robin
possa ser um bom conceito, o sidecar
é uma combinação perfeita para a própria tecnologia - não existe um nome "bom" para o maincar
. Não existe um maincar
, e obviamente Batman
, Lonely Wolf
, Solitude
, Driver
e Solo
não devem ser usados para nomear uma parte que não é um carro lateral.
O Facebook usou display
e interactivity
, e essa pode ser a melhor opção para todos nós.
Se você tem um bom nome para mim - deixe nos comentários
Árvore tremendo
É mais sobre a separação de preocupações do ponto de vista do bundler . Vamos imaginar que você tem Batman
e Robin
. And stuff.js
Então você pode tentar dividir o código do componente para implementar um side-car
Em resumo - o código acima funcionaria, mas não fará "o trabalho".
- se você estiver usando apenas
batman
do stuff.js
- o tremor da árvore manteria apenas isso. - se você estiver usando apenas
robin
do stuff.js
- o tremor da árvore manteria apenas isso. - mas se você estiver usando os dois, mesmo em partes diferentes - ambos serão agrupados em uma primeira ocorrência do
stuff.js
, ou seja, o pacote principal .
A trepidação de árvores não é favorável à divisão de códigos. Você precisa separar as preocupações por arquivos.
Desimportar
Outra coisa, esquecida por todos, é o custo do javascript. Era bastante comum na era do jQuery, a era da carga útil do jsonp
para carregar o script (com a carga útil do json
), obter a carga útil e remover o script.
Hoje em dia todos nós import
scripts, e eles serão importados para sempre, mesmo que não sejam mais necessários.
Como eu disse antes - há muito JS e, mais cedo ou mais tarde, com a navegação contínua, você carregará tudo. Deveríamos encontrar uma maneira de cancelar a importação e não precisar mais de chunk, limpando todos os caches internos e liberando memória para tornar a web mais confiável e não esmagar aplicativos com exceções de falta de memória.
Provavelmente, a capacidade de desinstalar (o webpack pode fazer isso ) é um dos motivos pelos quais devemos nos ater à API baseada em componentes , desde que ela nos permita lidar com a unmount
.
Até agora - os padrões dos módulos ESM não têm nada sobre isso - nem sobre controle de cache, nem sobre a reversão da ação de importação.
Criando uma biblioteca habilitada para sidecar
Até hoje, existe apenas uma maneira de criar uma biblioteca habilitada para sidecar
:
- divida seu componente em partes
- expor uma parte
main
e uma parte connected
(para não quebrar a API) via index
- expor um
sidecar
através de um ponto de entrada separado. - no código de destino - importe a parte
main
e a agitação da árvore sidecar
deve cortar uma parte connected
.
Essa agitação da árvore do tempo deve funcionar corretamente, e o único problema - é como nomear a parte main
.
export const Main = ({sidecar, ...props}) => ( <div> {sidecar} .... </div> );
import Main from './Component'; import Sidecar from './Sidecar'; export const Connected = props => ( <Main sidecar={<Sidecar />} {...props} /> );
export * from './Main'; export * from './Connected';
import * from './Sidecar';
Em suma, a mudança pode ser representada através de uma pequena comparação
A dynamic import
teoricamente dynamic import
poderia ser usada dentro de node_modules, tornando o processo de montagem mais transparente.
Enfim - nada mais é do que slot
padrão de children
/ slot
, tão comum no React.
O futuro
Facebook
provou que a ideia está certa. Se você ainda não viu esse vídeo, faça-o agora. Acabei de explicar a mesma idéia de um ângulo um pouco diferente (e comecei a escrever este artigo uma semana antes da conferência F8).
No momento, é necessário que algumas alterações de código sejam aplicadas à sua base de código. Ele exige uma separação mais explícita das preocupações para separá-las e permitir que os códigos sejam divididos não horizontalmente, mas verticalmente, enviando menos códigos para uma experiência maior do usuário.
Sidecar
, provavelmente, é a única maneira, exceto o SSR da velha escola, de lidar com grandes bases de códigos. Última chance de enviar uma quantidade mínima de código, quando você tiver muito.
Isso poderia tornar um aplicativo BIG menor e um aplicativo PEQUENO ainda mais pequeno.
Há 10 anos, o site médio estava "pronto" em 300ms e estava realmente pronto alguns milissegundos depois. Hoje, segundos e até mais de 10 segundos são os números comuns. Que pena.
Vamos fazer uma pausa e pensar - como poderíamos resolver o problema e tornar o UX ótimo novamente ...

Geral
- A divisão de código de componente é a ferramenta mais poderosa, oferecendo a capacidade de dividir algo completamente , mas tem um custo - você pode não exibir nada, exceto uma página em branco ou um esqueleto por um tempo. Essa é uma separação horizontal.
- A divisão do código da biblioteca pode ajudar quando a divisão do componente não. Essa é uma separação horizontal.
- O código transferido para um side-car completaria a imagem e pode permitir que você forneça uma experiência muito melhor ao usuário. Mas também exigiria algum esforço de engenharia. Essa é uma separação vertical.
Vamos conversar sobre isso .
Pare! E os problemas que você tentou resolver?
Bem, essa foi apenas a primeira parte. Estamos no final do jogo agora , levaria mais algumas semanas para escrever a segunda parte desta proposta. Enquanto isso ... entre no carro lateral!