React Code Split em 2019

É 2019! Todo mundo pensa que sabe a divisão de código. Então - vamos conferir!



O que significa divisão de código?


Em resumo, a divisão de código é quase não carregar uma coisa toda. Então você está lendo esta página e não precisa carregar um site inteiro. Quando você seleciona uma única linha de um banco de dados - não precisa pegar tudo.
Óbvio? A divisão de código também é bastante óbvia, não apenas sobre seus dados, mas sobre seu código.


Quem (o quê?) Está criando código dividido?


React.lazy ? Não - ele apenas usa. A divisão de código está sendo feita no nível do empacotador - webpack, pacote ou apenas seu sistema de arquivos no caso de módulos esm "nativos". A divisão de código é apenas arquivos, arquivos que você pode carregar em algum lugar "mais tarde". Então - para as perguntas " O que está alimentando a divisão de código? " - a resposta é - um "empacotador".


Quem (o que) está usando a divisão de código?


React.lazy está usando. Basta usar a divisão de código do seu bundler. Apenas chamando import quando é renderizado. E isso é tudo.


O que é o React-loadable?


React.lazy substituiu-o. E forneceu mais recursos, como Suspense para controlar o estado de carregamento. Então - use React.Lazy .


Sim, é tudo. Obrigado pela leitura e tenha um bom dia.

Por que o artigo não está terminado?


Bem. Existem algumas zonas cinzentas sobre React.lazy e divisão de código que esqueci de mencionar.


Zona 1 cinza - teste


Não é fácil testar o React.lazy devido à sua assincronidade . Seria apenas "vazio", desde que ainda não esteja carregado (mesmo que esteja) - Promises e retornos de import , e preguiçoso aceita promessas , que sempre são executadas no próximo tick .


Solução proposta? Você não acreditaria, mas a solução proposta é usar as tabelas síncronas - consulte solicitação de recebimento . Então - vamos fazer as nossas imports sincrônicas !!! (para corrigir problemas preguiçosos nos testes ou em qualquer outro caso do lado do servidor)


 const LazyText = lazy(() => ({ then(cb) { cb({default: Text}); // this is "sync" thenable }, })); const root = ReactTestRenderer.create( <Suspense fallback={<Text text="Loading..." />}> <LazyText text="Hi" /> // this lazy is not very lazy </Suspense>, ); 

Não é difícil converter a função de importação em uma tabela síncrona memorizada.


 const syncImport = (importFn) => { let preloaded = undefined; const promise = importFn().then(module => preloaded = module); // ^ "auto" import and "cache" promise return () => preloaded ? { then: () => preloaded } : promise; // ^ return sync thenable then possible } const lazyImport = isNode ? syncImport : a => a; // ^ sync for node, async for browser const LazyComponent = React.lazy(lazyImport(() => import('./file')); 

Zona cinza 2 - SSR


Se você não precisa de SSR - continue lendo o artigo!

React.lazy é compatível com SSR. Mas exige que o Suspense funcione, e o Suspense NÃO é amigável para o servidor .


Existem 2 soluções:


  • Substitua Suspense por Fragment, por meio de zombaria, por exemplo. Em seguida, use a versão alterada da import com síncrona para fazer com que o preguiçoso também se comporte de forma síncrona.
     import React from 'react'; const realLazy = React.lazy; React.lazy = importer => realLazy(syncImport(importer)); React.Suspense = React.Fragment; // :P // ^ React SSR just got fixed :D 

Essa é uma boa opção, mas não seria muito amigável para o cliente. Porque Vamos definir a segunda solução possível:


  • Use uma biblioteca especializada para rastrear scripts, pedaços e estilos usados ​​e carregá-los no lado do cliente (especialmente estilos!) Antes de reagir à hidratação. Ou então - você faria buracos vazios em vez de seus componentes divididos por código. Mais uma vez - você não carregou o código que acabou de dividir, portanto, não pode renderizar o que deseja.

Veja as bibliotecas de divisão de código


  • Componente universal - a biblioteca mais antiga e ainda atualizável. Ele "inventou" a divisão de código em termos de Webpack ensinado a dividir código.
  • React-loadable - muito popular, mas uma biblioteca não mantida. Código feito cuspindo uma coisa popular. Os problemas estão fechados, portanto não há comunidade por perto.
  • Componentes carregáveis - uma biblioteca completa de recursos, é um prazer usar, com a comunidade mais ativa do mercado.
  • Componente importado - uma única biblioteca, não vinculada ao Webpack, ou seja, capaz de lidar com encomendas ou esm.
  • React-async-component - biblioteca já inoperante (ainda popular), que causou um impacto significativo em tudo que envolve divisão de código, passagem de árvore React personalizada e SSR.
  • Outra biblioteca - havia muitas bibliotecas, muitas das quais não sobreviveram à evolução do Webpack ou ao React 16 - eu não as listei aqui, mas se você conhece um bom candidato - apenas me DM.

Qual biblioteca escolher?


É fácil - não pode ser carregado por reação - é pesado, sem manutenção e obsoleto, mesmo que ainda seja mega popular. (e obrigado por popularizar a divisão do código, mais uma vez)


Componentes carregáveis - pode ser uma escolha muito boa. Está muito bem escrito, é mantido ativamente e suporta tudo pronto para uso. Suporte para "importações dinâmicas completas", permitindo importar arquivos dependendo dos acessórios fornecidos, mas, portanto, não tipáveis. Suporta Suspense, portanto, poderia substituir React.lazy.


Componente universal - na verdade "inventores" de importações dinâmicas completas - eles o implementaram no Webpack. E muitas outras coisas em nível baixo - eles fizeram isso. Eu diria - esta biblioteca é um pouco incondicional e um pouco menos amigável. A documentação dos componentes carregáveis ​​é imbatível. Vale a pena usar essa biblioteca e ler a documentação - há muitos detalhes que você deve conhecer ...


Reagir-componente-importado - é um pouco estranho. É independente de empacotador, portanto nunca quebraria (não há nada a quebrar), funcionaria com o Webpack 5 e 55, mas isso tem um custo. Embora as bibliotecas anteriores durante o SSR adicionem todos os scripts usados ​​ao corpo da página, você poderá carregar todos os scripts em paralelo - os arquivos importados não sabem os nomes dos arquivos e chamarão as "importações" originais (é por isso que o pacote independente) para carregar os pedaços usados, mas capaz de fazer chamadas apenas de dentro do pacote principal - para que todos os scripts adicionais sejam carregados somente após o download e a execução do principal. Não suporta importações dinâmicas completas, como React.lazy e, como resultado - tipificável. Também suporta Suspense. Usa tabelas de tempo síncronas no SSR. Ele também possui uma abordagem absolutamente diferente para CSS e suporte perfeito para renderização de fluxo.


Não há diferença de qualidade ou popularidade entre as bibliotecas listadas, e todos somos bons amigos - então escolha seu coração.


Zona cinza 3 - renderização híbrida


SSR é uma coisa boa, mas, você sabe, difícil. Projetos pequenos podem querer ter um SSR - há muitos motivos para tê-lo - mas eles podem não querer configurá-lo e mantê-lo.


SSR pode ser muito, MUITO difícil. Tente razzle ou vá com Next.js se você quiser uma vitória rápida.

Portanto, a minha solução mais fácil para SSR, especialmente para SPA simples, seria a pré-renderização. Como abrir seu SPA em um navegador e clicar no botão "Salvar". Como:


  • React-snap - usa marionetista (também conhecido como Chrome sem cabeça) para renderizar sua página em um "navegador" e salva um resultado como uma página HTML estática.
  • Rendertron - que faz o mesmo, mas de uma maneira diferente (em nuvem ).

A pré-renderização é "SSR" sem "Servidor". É SSR usando um cliente. Magia! E trabalhando fora da caixa ... ... ... ... mas não para cuspir código.
Então - você acabou de renderizar sua página em um navegador, salvou o HTML e pediu para carregar o mesmo material. Mas o código específico do servidor (para coletar todos os pedaços usados) não foi usado, pois NÃO HÁ SERVIDOR !



Na parte anterior, apontei as bibliotecas que são vinculadas ao webpack em termos de coleta de informações sobre os pedaços usados ​​- eles não conseguiam lidar com a renderização híbrida.


A versão 2 dos componentes carregáveis ​​(incompatível com a versão atual 5) foi parcialmente suportada pelo react-snap. O suporte foi embora.

O componente reagir-importado pode lidar com esse caso, desde que não esteja vinculado ao bundler / lado; portanto, não há diferença para SSR ou híbrido, mas apenas para reagir-snap, desde que ele suporte a "hidratação de estado", enquanto rendertron não.


Essa capacidade de reagir-componente importado foi encontrada durante a redação deste artigo, ela não era conhecida antes - veja o exemplo . É bem fácil

E aqui você precisa usar outra solução, que é perpendicular a todas as outras bibliotecas.


Reagir-componente pré-renderizado


Essa biblioteca foi criada para hidratação parcial e pode reidratar parcialmente o aplicativo, mantendo o restante ainda desidratado. E funciona para renderizadores SSR e Hybrid sem nenhuma diferença.
A ideia é simples:


  • durante SSR - renderize o componente envolvido com um <div />
  • no cliente - encontre essa div e use innerHTML até que o Component esteja pronto para substituir o HTML morto.
  • você não precisa carregar e aguardar que um pedaço com componente dividido antes de hydrate NÃO torne um buraco branco em vez dele - basta usar HTML pré-renderizado, que é absolutamente igual ao que um componente real renderizaria e que já existe - ele vem com uma resposta do servidor (ou hídrica).

É por isso que precisamos aguardar o carregamento de todos os blocos antes de se hidratar - para corresponder ao HTML renderizado pelo servidor. É por isso que podemos usar pedaços de HTML renderizados pelo servidor até que o cliente não esteja pronto - é igual ao que apenas produziremos.

 import {PrerenderedComponent} from 'react-prerendered-component'; const importer = memoizeOne(() => import('./Component')); // ^ it's very important to keep the "one" promise const Component = React.lazy(importer); // or use any other library with ".prefetch" support // all libraries has it (more or less) const App = () => ( <PrerenderedComponent live={importer()}> {/* ^ shall return the same promise */ } <Component /> {/* ^ would be rendered when a component goes "live" */ } </PrerenderedComponent> ); 

outro artigo sobre essa tecnologia , você pode ler. Mas o principal aqui - resolve o "Flash Of Unloaded Content" em outro, não muito comum para bibliotecas de divisão de código . Esteja aberto para novas soluções.


TLDR?


  • não use react-loadable, não agregaria nenhum valor valioso
  • React.lazy é bom, mas muito simples, ainda.
  • SSR é uma coisa difícil, e você deve saber disso
  • A renderização híbrida orientada por marionetistas é uma coisa. Às vezes, coisa ainda mais difícil.

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


All Articles