Como acelerar seu app Next.js com estratégias de divisão de código e carregamento inteligente.
O que você aprenderá?
Esta postagem explica diferentes tipos de divisão de código e como usar importações dinâmicas para acelerar seus apps do Next.js.
Divisão de código baseada em rota e em componente
Por padrão, o Next.js divide seu JavaScript em partes separadas para cada rota. Quando os usuários carregam seu aplicativo, o Next.js envia apenas o código necessário para a rota inicial. Quando os usuários navegam pelo aplicativo, eles buscam os blocos associados às outras rotas. A divisão de código baseada em rota minimiza a quantidade de script que precisa ser analisada e compilada de uma vez, o que resulta em tempos de carregamento da página mais rápidos.
Embora a divisão de código baseada em rota seja um bom padrão, você pode otimizar ainda mais o processo de carregamento com a divisão de código no nível do componente. Se você tiver componentes grandes no app, é uma boa ideia dividi-los em partes separadas. Dessa forma, todos os componentes grandes que não são essenciais ou renderizam apenas em determinadas interações do usuário (como clicar em um botão) podem ser carregados com atraso.
O Next.js oferece suporte a import()
dinâmico,
que permite importar módulos JavaScript (incluindo componentes do React)
de forma dinâmica e carregar cada importação como um bloco separado. Isso permite
a divisão de código no nível do componente e permite controlar o carregamento de recursos para
que os usuários façam o download apenas do código necessário para a parte do site que
eles estão visualizando. No Next.js, esses componentes são renderizados do lado do servidor
(SSR, na sigla em inglês)
por padrão.
Importações dinâmicas em ação
Esta postagem inclui várias versões de um app de exemplo que consiste em uma página simples com um botão. Ao clicar no botão, você vê um filhote de cachorro fofo. À medida que você avança em cada versão do app, vai notar como as importações dinâmicas são diferentes das importações estáticas e como trabalhar com elas.
Na primeira versão do app, o filhote vive em components/Puppy.js
. Para
mostrar o filhote na página, o app importa o componente Puppy
em
index.js
com uma instrução de importação estática:
import Puppy from "../components/Puppy";
Para saber como o Next.js agrupa o app, inspecione o trace de rede no DevTools:
Para visualizar o site, pressione View App. Em seguida, pressione Fullscreen .
Pressione "Control+Shift+J" (ou "Command+Option+J" no Mac) para abrir as Ferramentas do desenvolvedor.
Clique na guia Rede.
Marque a caixa de seleção Desativar cache.
Recarregue a página.
Quando você carrega a página, todo o código necessário, incluindo o componente Puppy.js
, é agrupado em index.js
:
Quando você pressiona o botão Click me, apenas a solicitação do JPEG do filhote de cachorro é adicionada à guia Network:
A desvantagem dessa abordagem é que, mesmo que os usuários não cliquem no botão para
ver o filhote, eles precisam carregar o componente Puppy
porque ele está incluído em
index.js
. Nesse pequeno exemplo, isso não é um problema, mas em aplicativos reais,
muitas vezes é uma grande melhoria carregar componentes grandes apenas quando
necessário.
Agora confira uma segunda versão do app, em que a importação estática é
substituída por uma importação dinâmica. O Next.js inclui next/dynamic
, que permite
usar importações dinâmicas para qualquer componente no Next:
import Puppy from "../components/Puppy";
import dynamic from "next/dynamic";
// ...
const Puppy = dynamic(import("../components/Puppy"));
Siga as etapas do primeiro exemplo para inspecionar o trace de rede.
Quando você carrega o app pela primeira vez, apenas index.js
é transferido por download. Dessa vez, ele é
0,5 KB menor (foi de 37,9 KB para 37,4 KB) porque não
inclui o código do componente Puppy
:
O componente Puppy
agora está em um bloco separado, 1.js
, que é carregado apenas
quando você pressiona o botão:
Em aplicativos reais, os componentes geralmente são muito maiores, e o carregamento lento deles pode reduzir o payload inicial do JavaScript em centenas de kilobytes.
Importações dinâmicas com indicador de carregamento personalizado
Ao carregar recursos de forma lenta, é recomendável fornecer um indicador de carregamento
caso haja atrasos. No Next.js, é possível fazer isso fornecendo um
argumento adicional à função dynamic()
:
const Puppy = dynamic(() => import("../components/Puppy"), {
loading: () => <p>Loading...</p>
});
Para conferir o indicador de carregamento em ação, simule uma conexão de rede lenta nas Ferramentas do desenvolvedor:
Para visualizar o site, pressione View App. Em seguida, pressione Fullscreen .
Pressione "Control+Shift+J" (ou "Command+Option+J" no Mac) para abrir as Ferramentas do desenvolvedor.
Clique na guia Rede.
Marque a caixa de seleção Desativar cache.
Na lista suspensa Limitação, selecione 3G rápido.
Pressione o botão Clique em mim.
Agora, quando você clica no botão, leva um tempo para carregar o componente, e o app mostra a mensagem "Carregando…" nesse meio tempo.
Importações dinâmicas sem SSR
Se você precisar renderizar um componente apenas no lado do cliente (por exemplo, um widget
de chat), defina a opção ssr
como false
:
const Puppy = dynamic(() => import("../components/Puppy"), {
ssr: false,
});
Conclusão
Com suporte a importações dinâmicas, o Next.js oferece a divisão de código no nível do componente, que pode minimizar os payloads JavaScript e melhorar o tempo de carregamento do aplicativo. Todos os componentes são renderizados no servidor por padrão, e você pode desativar essa opção sempre que necessário.